ff82e2e5219c0fbdc4e80f86877da95f0352f90d
[profile/ivi/syslinux.git] / ldlinux.asm
1 ; -*- fundamental -*- (asm-mode sucks)
2 ; $Id$
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-2002  H. Peter Anvin
20 ;
21 ;  This program is free software; you can redistribute it and/or modify
22 ;  it under the terms of the GNU General Public License as published by
23 ;  the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
24 ;  USA; either version 2 of the License, or (at your option) any later
25 ;  version; incorporated herein by reference.
26
27 ; ****************************************************************************
28
29 ;
30 ; Some semi-configurable constants... change on your own risk.  Most are imposed
31 ; by the kernel.
32 ;
33 max_cmd_len     equ 255                 ; Must be odd; 255 is the kernel limit
34 retry_count     equ 6                   ; How patient are we with the disk?
35 HIGHMEM_MAX     equ 037FFFFFFh          ; DEFAULT highest address for an initrd
36 DEFAULT_BAUD    equ 9600                ; Default baud rate for serial port
37 BAUD_DIVISOR    equ 115200              ; Serial port parameter
38 ;
39 ; Should be updated with every release to avoid bootsector/SYS file mismatch
40 ;
41 %define version_str     VERSION         ; Must be 4 characters long!
42 %define date            DATE_STR        ; Defined from the Makefile
43 %define year            '2002'
44 ;
45 ; Debgging stuff
46 ;
47 ; %define debug 1                       ; Uncomment to enable debugging
48 ;
49 ; ID for SYSLINUX (reported to kernel)
50 ;
51 syslinux_id     equ 031h                ; SYSLINUX (3) version 1.x (1)
52 ;
53 ; Segments used by Linux
54 ;
55 ; Note: the real_mode_seg is supposed to be 9000h, but some device drivers
56 ; hog some of high memory.  Therefore, we load it at 7000:0000h and copy
57 ; it before starting the Linux kernel.
58 ;
59 real_mode_seg   equ 7000h
60 fake_setup_seg  equ real_mode_seg+020h
61
62                 struc real_mode_seg_t
63                 resb 20h-($-$$)         ; org 20h
64 kern_cmd_magic  resw 1                  ; 0020 Magic # for command line
65 kern_cmd_offset resw 1                  ; 0022 Offset for kernel command line
66                 resb 497-($-$$)         ; org 497d
67 bs_setupsecs    resb 1                  ; 01F1 Sectors for setup code (0 -> 4)
68 bs_rootflags    resw 1                  ; 01F2 Root readonly flag
69 bs_syssize      resw 1                  ; 01F4
70 bs_swapdev      resw 1                  ; 01F6 Swap device (obsolete)
71 bs_ramsize      resw 1                  ; 01F8 Ramdisk flags, formerly ramdisk size
72 bs_vidmode      resw 1                  ; 01FA Video mode
73 bs_rootdev      resw 1                  ; 01FC Root device
74 bs_bootsign     resw 1                  ; 01FE Boot sector signature (0AA55h)
75 su_jump         resb 1                  ; 0200 0EBh
76 su_jump2        resb 1                  ; 0201 Size of following header
77 su_header       resd 1                  ; 0202 New setup code: header
78 su_version      resw 1                  ; 0206 See linux/arch/i386/boot/setup.S
79 su_switch       resw 1                  ; 0208
80 su_setupseg     resw 1                  ; 020A
81 su_startsys     resw 1                  ; 020C
82 su_kver         resw 1                  ; 020E Kernel version pointer
83 su_loader       resb 1                  ; 0210 Loader ID
84 su_loadflags    resb 1                  ; 0211 Load high flag
85 su_movesize     resw 1                  ; 0212
86 su_code32start  resd 1                  ; 0214 Start of code loaded high
87 su_ramdiskat    resd 1                  ; 0218 Start of initial ramdisk
88 su_ramdisklen   equ $                   ; Length of initial ramdisk
89 su_ramdisklen1  resw 1                  ; 021C
90 su_ramdisklen2  resw 1                  ; 021E
91 su_bsklugeoffs  resw 1                  ; 0220
92 su_bsklugeseg   resw 1                  ; 0222
93 su_heapend      resw 1                  ; 0224
94 su_pad1         resw 1                  ; 0226
95 su_cmd_line_ptr resd 1                  ; 0228
96 su_ramdisk_max  resd 1                  ; 022C
97                 resb (9000h-12)-($-$$)  ; The setup is up to 32K long
98 linux_stack     equ $                   ; 8FF4
99 linux_fdctab    equ $
100                 resb 9000h-($-$$)
101 cmd_line_here   equ $                   ; 9000 Should be out of the way
102                 endstruc
103
104 ;
105 ; Kernel command line signature
106 ;
107 CMD_MAGIC       equ 0A33Fh              ; Command line magic
108
109 ;
110 ; Magic number of su_header field
111 ;
112 HEADER_ID       equ 'HdrS'              ; HdrS (in littleendian hex)
113
114 ;
115 ; Flags for the su_loadflags field
116 ;
117 LOAD_HIGH       equ 01h                 ; Large kernel, load high
118 CAN_USE_HEAP    equ 80h                 ; Boot loader reports heap size
119
120 ;
121 ; The following structure is used for "virtual kernels"; i.e. LILO-style
122 ; option labels.  The options we permit here are `kernel' and `append
123 ; Since there is no room in the bottom 64K for all of these, we
124 ; stick them at vk_seg:0000 and copy them down before we need them.
125 ;
126 ; Note: this structure can be added to, but it must 
127 ;
128 %define vk_power        7               ; log2(max number of vkernels)
129 %define max_vk          (1 << vk_power) ; Maximum number of vkernels
130 %define vk_shift        (16-vk_power)   ; Number of bits to shift
131 %define vk_size         (1 << vk_shift) ; Size of a vkernel buffer
132
133                 struc vkernel
134 vk_vname:       resb 11                 ; Virtual name **MUST BE FIRST!**
135 vk_rname:       resb 11                 ; Real name
136 vk_appendlen:   resw 1
137                 alignb 4
138 vk_append:      resb max_cmd_len+1      ; Command line
139                 alignb 4
140 vk_end:         equ $                   ; Should be <= vk_size
141                 endstruc
142
143 %if (vk_end > vk_size) || (vk_size*max_vk > 65536)
144 %error "Too many vkernels defined, reduce vk_power"
145 %endif
146
147 ;
148 ; Segment assignments in the bottom 640K
149 ; Stick to the low 512K in case we're using something like M-systems flash
150 ; which load a driver into low RAM (evil!!)
151 ;
152 ; 0000h - main code/data segment (and BIOS segment)
153 ; 7000h - real_mode_seg
154 ;
155 fat_seg         equ 5000h               ; 128K area for FAT (2x64K)
156 vk_seg          equ 4000h               ; Virtual kernels
157 xfer_buf_seg    equ 3000h               ; Bounce buffer for I/O to high mem
158 comboot_seg     equ 2000h               ; COMBOOT image loading zone
159
160 ;
161 ; For our convenience: define macros for jump-over-unconditinal jumps
162 ;
163 %macro  jmpz    1
164         jnz %%skip
165         jmp %1
166 %%skip:
167 %endmacro
168
169 %macro  jmpnz   1
170         jz %%skip
171         jmp %1
172 %%skip:
173 %endmacro
174
175 %macro  jmpe    1
176         jne %%skip
177         jmp %1
178 %%skip:
179 %endmacro
180
181 %macro  jmpne   1
182         je %%skip
183         jmp %1
184 %%skip:
185 %endmacro
186
187 %macro  jmpc    1
188         jnc %%skip
189         jmp %1
190 %%skip:
191 %endmacro
192
193 %macro  jmpnc   1
194         jc %%skip
195         jmp %1
196 %%skip:
197 %endmacro
198
199 %macro  jmpb    1
200         jnb %%skip
201         jmp %1
202 %%skip:
203 %endmacro
204
205 %macro  jmpnb   1
206         jb %%skip
207         jmp %1
208 %%skip:
209 %endmacro
210
211 ;
212 ; Macros similar to res[bwd], but which works in the code segment (after
213 ; section .text)
214 ;
215 %macro  zb      1
216         times %1 db 0
217 %endmacro
218
219 %macro  zw      1
220         times %1 dw 0
221 %endmacro
222
223 %macro  zd      1
224         times %1 dd 0
225 %endmacro
226
227 ; ---------------------------------------------------------------------------
228 ;   BEGIN THE BIOS/CODE/DATA SEGMENT
229 ; ---------------------------------------------------------------------------
230                 absolute 4*1Eh          ; In the interrupt table
231 fdctab          equ $
232 fdctab1         resw 1
233 fdctab2         resw 1
234
235                 absolute 0400h
236 serial_base     resw 4                  ; Base addresses for 4 serial ports
237
238                 absolute 0484h
239 BIOS_vidrows    resb 1                  ; Number of screen rows
240
241 ;
242 ; Memory below this point is reserved for the BIOS and the MBR
243 ;
244                 absolute 1000h
245 trackbuf        equ $                   ; Track buffer goes here
246 trackbufsize    equ 16384               ; Safe size of track buffer
247 ;               trackbuf ends at 5000h
248
249
250 ;
251 ; Constants for the xfer_buf_seg
252 ;
253 ; The xfer_buf_seg is also used to store message file buffers.  We
254 ; need two trackbuffers (text and graphics), plus a work buffer
255 ; for the graphics decompressor.
256 ;
257 xbs_textbuf     equ 0                   ; Also hard-coded, do not change
258 xbs_vgabuf      equ trackbufsize
259 xbs_vgatmpbuf   equ 2*trackbufsize
260
261
262                 absolute 5000h          ; Here we keep our BSS stuff
263 VKernelBuf:     resb vk_size            ; "Current" vkernel
264                 alignb 4
265 AppendBuf       resb max_cmd_len+1      ; append=
266 KbdMap          resb 256                ; Keyboard map
267 FKeyName        resb 10*16              ; File names for F-key help
268 NumBuf          resb 15                 ; Buffer to load number
269 NumBufEnd       resb 1                  ; Last byte in NumBuf
270                 alignb 4
271 PartInfo        resb 16                 ; Partition table entry
272 E820Buf         resd 5                  ; INT 15:E820 data buffer
273 HiLoadAddr      resd 1                  ; Address pointer for high load loop
274 HighMemSize     resd 1                  ; End of memory pointer (bytes)
275 RamdiskMax      resd 1                  ; Highest address for a ramdisk
276 KernelSize      resd 1                  ; Size of kernel (bytes)
277 KernelName      resb 12                 ; Mangled name for kernel
278                                         ; (note the spare byte after!)
279 RootDir         equ $                   ; Location of root directory
280 RootDir1        resw 1
281 RootDir2        resw 1
282 DataArea        equ $                   ; Location of data area
283 DataArea1       resw 1
284 DataArea2       resw 1
285 FBytes          equ $                   ; Used by open/getc
286 FBytes1         resw 1
287 FBytes2         resw 1
288 RootDirSize     resw 1                  ; Root dir size in sectors
289 DirScanCtr      resw 1                  ; Used while searching directory
290 DirBlocksLeft   resw 1                  ; Ditto
291 EndofDirSec     resw 1                  ; = trackbuf+bsBytesPerSec-31
292 RunLinClust     resw 1                  ; Cluster # for LDLINUX.SYS
293 ClustSize       resw 1                  ; Bytes/cluster
294 SecPerClust     resw 1                  ; Same as bsSecPerClust, but a word
295 NextCluster     resw 1                  ; Pointer to "nextcluster" routine
296 BufSafe         resw 1                  ; Clusters we can load into trackbuf
297 BufSafeSec      resw 1                  ; = how many sectors?
298 BufSafeBytes    resw 1                  ; = how many bytes?
299 EndOfGetCBuf    resw 1                  ; = getcbuf+BufSafeBytes
300 KernelClust     resw 1                  ; Kernel size in clusters
301 ClustPerMoby    resw 1                  ; Clusters per 64K
302 FClust          resw 1                  ; Number of clusters in open/getc file
303 FNextClust      resw 1                  ; Pointer to next cluster in d:o
304 FPtr            resw 1                  ; Pointer to next char in buffer
305 CmdOptPtr       resw 1                  ; Pointer to first option on cmd line
306 KernelCNameLen  resw 1                  ; Length of unmangled kernel name
307 InitRDCNameLen  resw 1                  ; Length of unmangled initrd name
308 NextCharJump    resw 1                  ; Routine to interpret next print char
309 SetupSecs       resw 1                  ; Number of setup sectors
310 SavedSP         resw 1                  ; Our SP while running a COMBOOT image
311 A20Test         resw 1                  ; Counter for testing status of A20
312 CmdLineLen      resw 1                  ; Length of command line including null
313 GraphXSize      resw 1                  ; Width of splash screen file
314 VGAPos          resw 1                  ; Pointer into VGA memory
315 VGACluster      resw 1                  ; Cluster pointer for VGA image file
316 VGAFilePtr      resw 1                  ; Pointer into VGAFileBuf
317 TextAttrBX      equ $
318 TextAttribute   resb 1                  ; Text attribute for message file
319 TextPage        resb 1                  ; Active display page
320 CursorDX        equ $
321 CursorCol       resb 1                  ; Cursor column for message file
322 CursorRow       resb 1                  ; Cursor row for message file
323 ScreenSize      equ $
324 VidCols         resb 1                  ; Columns on screen-1
325 VidRows         resb 1                  ; Rows on screen-1
326 FlowControl     equ $
327 FlowOutput      resb 1                  ; Outputs to assert for serial flow
328 FlowInput       resb 1                  ; Input bits for serial flow
329 FlowIgnore      resb 1                  ; Ignore input unless these bits set
330 RetryCount      resb 1                  ; Used for disk access retries
331 KbdFlags        resb 1                  ; Check for keyboard escapes
332 LoadFlags       resb 1                  ; Loadflags from kernel
333 A20Tries        resb 1                  ; Times until giving up on A20
334 FuncFlag        resb 1                  ; Escape sequences received from keyboard
335 DisplayMask     resb 1                  ; Display modes mask
336 MNameBuf        resb 11                 ; Generic mangled file name buffer
337 InitRD          resb 11                 ; initrd= mangled name
338 KernelCName     resb 13                 ; Unmangled kernel name
339 InitRDCName     resb 13                 ; Unmangled initrd name
340 TextColorReg    resb 17                 ; VGA color registers for text mode
341 VGAFileBuf      resb 13                 ; Unmangled VGA image name
342 VGAFileBufEnd   equ $
343 VGAFileMBuf     resb 11                 ; Mangled VGA image name
344
345                 section .text
346                 org 7C00h
347 StackBuf        equ $                   ; Start the stack here (grow down - 4K)
348
349 ;
350 ; Primary entry point.  Tempting as though it may be, we can't put the
351 ; initial "cli" here; the jmp opcode in the first byte is part of the
352 ; "magic number" (using the term very loosely) for the DOS superblock.
353 ;
354 bootsec         equ $
355                 jmp short start         ; 2 bytes
356                 nop                     ; 1 byte
357 ;
358 ; "Superblock" follows -- it's in the boot sector, so it's already
359 ; loaded and ready for us
360 ;
361 bsOemName       db 'SYSLINUX'           ; The SYS command sets this, so...
362 superblock      equ $
363 bsBytesPerSec   zw 1
364 bsSecPerClust   zb 1
365 bsResSectors    zw 1
366 bsFATs          zb 1
367 bsRootDirEnts   zw 1
368 bsSectors       zw 1
369 bsMedia         zb 1
370 bsFATsecs       zw 1
371 bsSecPerTrack   zw 1
372 bsHeads         zw 1
373 bsHiddenSecs    equ $
374 bsHidden1       zw 1
375 bsHidden2       zw 1
376 bsHugeSectors   equ $
377 bsHugeSec1      zw 1
378 bsHugeSec2      zw 1
379 bsDriveNumber   zb 1
380 bsReserved1     zb 1
381 bsBootSignature zb 1                    ; 29h if the following fields exist
382 bsVolumeID      zd 1
383 bsVolumeLabel   zb 11
384 bsFileSysType   zb 8                    ; Must be FAT12 for this version
385 superblock_len  equ $-superblock
386 ;
387 ; Note we don't check the constraints above now; we did that at install
388 ; time (we hope!)
389 ;
390
391 ;floppy_table   equ $                   ; No sense in wasting memory, overwrite start
392
393 start:
394                 cli                     ; No interrupts yet, please
395                 cld                     ; Copy upwards
396 ;
397 ; Set up the stack
398 ;
399                 xor cx,cx
400                 mov ss,cx
401                 mov sp,StackBuf         ; Just below BSS
402                 mov es,cx
403 ;
404 ; DS:SI may contain a partition table entry.  Preserve it for us.
405 ;
406                 mov cl,8                ; Save partition info (CH == 0)
407                 mov di,PartInfo
408                 rep movsw
409 ;
410 ; Now sautee the BIOS floppy info block to that it will support decent-
411 ; size transfers; the floppy block is 11 bytes and is stored in the
412 ; INT 1Eh vector (brilliant waste of resources, eh?)
413 ;
414 ; Of course, if BIOSes had been properly programmed, we wouldn't have
415 ; had to waste precious boot sector space with this code.
416 ;
417 ; This code no longer fits.  Hope that noone really needs it anymore.
418 ; (If so, it needs serious updating.)  In fact, some indications is that
419 ; this code does more harm than good with all the new kinds of drives and
420 ; media.
421 ;
422 %ifdef SUPPORT_REALLY_BROKEN_BIOSES
423                 lds si,[ss:fdctab]      ; DS:SI -> original
424                 push ds                 ; Save on stack in case
425                 push si                 ; we have to bail
426                 push bx
427                 mov cx,6                ; 12 bytes
428                 mov di,floppy_table
429                 push di
430                 cld
431                 rep movsw               ; Faster to move words
432                 pop di
433                 mov ds,ax               ; Now we can point DS to here, too
434                 mov cl,[bsSecPerTrack]  ; Patch the sector count
435                 mov [di+4],cl
436                 mov [fdctab+2],ax       ; Segment 0
437                 mov [fdctab],di         ; offset floppy_block
438 %else
439                 mov ds,cx               ; CX == 0
440 %endif
441 ;
442 ; Ready to enable interrupts, captain
443 ;
444                 sti
445 ;
446 ; The drive number and possibly partition information was passed to us
447 ; by the BIOS or previous boot loader (MBR).  Current "best practice" is to
448 ; trust that rather than what the superblock contains.
449 ;
450 ; Would it be better to zero out bsHidden if we don't have a partition table?
451 ;
452 ; Note: di points to beyond the end of PartInfo
453 ;
454                 mov [bsDriveNumber],dl
455                 test dl,80h             ; If floppy disk (00-7F), assume no
456                 jz not_harddisk         ; partition table
457                 test byte [di-16],7Fh   ; Sanity check: "active flag" should
458                 jnz no_partition        ; be 00 or 80
459                 lea si,[di-8]           ; Partition offset (dword)
460                 mov di,bsHidden1
461                 mov cl,2                ; CH == 0
462                 rep movsw
463 no_partition:
464 ;
465 ; Get disk drive parameters (don't trust the superblock.)  Don't do this for
466 ; floppy drives -- INT 13:08 on floppy drives will (may?) return info about
467 ; what the *drive* supports, not about the *media*.  Fortunately floppy disks
468 ; tend to have a fixed, well-defined geometry which is stored in the superblock.
469 ;
470                 ; DL == drive # still
471                 mov ah,08h
472                 int 13h
473                 jc no_driveparm
474                 and ah,ah
475                 jnz no_driveparm
476                 inc dh                  ; Contains # of heads - 1
477                 mov [bsHeads],dh
478                 and cx,3fh
479                 mov [bsSecPerTrack],cx
480 no_driveparm:
481 not_harddisk:
482 ;
483 ; Now we have to do some arithmetric to figure out where things are located.
484 ; If Micro$oft had had brains they would already have done this for us,
485 ; and stored it in the superblock at format time, but here we go,
486 ; wasting precious boot sector space again...
487 ;
488 debugentrypt:
489                 xor ax,ax               ; INT 13:08 destroys ES
490                 mov es,ax
491                 mov al,[bsFATs]         ; Number of FATs (AH == 0)
492                 mul word [bsFATsecs]    ; Get the size of the FAT area
493                 add ax,[bsHidden1]      ; Add hidden sectors
494                 adc dx,[bsHidden2]
495                 add ax,[bsResSectors]   ; And reserved sectors
496                 adc dx,byte 0
497
498                 mov [RootDir1],ax       ; Location of root directory
499                 mov [RootDir2],dx
500                 mov [DataArea1],ax
501                 mov [DataArea2],dx
502                 push ax
503                 push dx
504
505                 mov ax,32               ; Size of a directory entry
506                 mul word [bsRootDirEnts]
507                 mov bx,[bsBytesPerSec]
508                 add ax,bx               ; Round up, not down
509                 dec ax
510                 div bx                  ; Now we have the size of the root dir
511                 mov [RootDirSize],ax
512                 mov [DirScanCtr],ax
513                 add bx,trackbuf-31
514                 mov [EndofDirSec],bx    ; End of a single directory sector
515
516                 add [DataArea1],ax
517                 adc word [DataArea2],byte 0
518
519                 pop dx                  ; Reload root directory starting point
520                 pop ax
521 ;
522 ; Now the fun begins.  We have to search the root directory for
523 ; LDLINUX.SYS and load the first sector, so we have a little more
524 ; space to have fun with.  Then we can go chasing through the FAT.
525 ; Joy!!
526 ;
527 sd_nextsec:     push ax
528                 push dx
529                 mov bx,trackbuf
530                 push bx
531                 call getonesec
532                 pop si
533 sd_nextentry:   cmp byte [si],0         ; Directory high water mark
534                 je kaboom
535                 test byte [si+11],18h   ; Must be a file
536                 jnz sd_not_file
537                 mov di,ldlinux_name
538                 mov cx,11
539                 push si
540                 repe cmpsb
541                 pop si
542                 je found_it
543 sd_not_file:    add si,byte 32          ; Distance to next
544                 cmp si,[EndofDirSec]
545                 jb sd_nextentry
546                 pop dx
547                 pop ax
548                 add ax,byte 1
549                 adc dx,byte 0
550                 dec word [DirScanCtr]
551                 jnz sd_nextsec
552 ;
553 ; kaboom: write a message and bail out.
554 ;
555 kaboom:
556                 xor si,si
557                 mov ss,si               
558                 mov sp,StackBuf         ; Reset stack
559                 mov ds,si               ; Reset data segment
560 .patch:         mov si,bailmsg
561                 call writestr           ; Returns with AL = 0
562                 cbw                     ; AH <- 0
563                 int 16h                 ; Wait for keypress
564                 int 19h                 ; And try once more to boot...
565 .norge:         jmp short .norge        ; If int 19h returned; this is the end
566
567 ;
568 ; found_it: now we compute the location of the first sector, then
569 ;           load it and JUMP (since we're almost out of space)
570 ;
571 found_it:       ; Note: we actually leave two words on the stack here
572                 ; (who cares?)
573                 xor ax,ax
574                 mov al,[bsSecPerClust]
575                 mov bp,ax               ; Load an entire cluster
576                 mov bx,[si+26]          ; First cluster
577                 mov [RunLinClust],bx    ; Save for later use
578                 dec bx                  ; First cluster is "cluster 2"
579                 dec bx
580                 mul bx
581                 add ax,[DataArea1]
582                 adc dx,[DataArea2]
583                 mov bx,ldlinux_sys
584                 call getlinsec
585                 mov si,bs_magic
586                 mov di,ldlinux_magic
587                 mov cx,magic_len
588                 repe cmpsb              ; Make sure that the bootsector
589                 jne kaboom              ; matches LDLINUX.SYS
590 ;
591 ; Done! Jump to the entry point!
592
593 ; Note that some BIOSes are buggy and run the boot sector at 07C0:0000
594 ; instead of 0000:7C00 and the like.  We don't want to add anything
595 ; more to the boot sector, so it is written to not assume a fixed
596 ; value in CS, but we don't want to deal with that anymore from now
597 ; on.
598 ;
599                 jmp 0:ldlinux_ent
600
601 ;
602 ;
603 ; writestr: write a null-terminated string to the console
604 ;
605 writestr:
606 wstr_1:         lodsb
607                 and al,al
608                 jz return
609                 mov ah,0Eh              ; Write to screen as TTY
610                 mov bx,0007h            ; White on black, current page
611                 int 10h
612                 jmp short wstr_1
613 ;
614 ; disk_error: decrement the retry count and bail if zero
615 ;
616 disk_error:     dec si                  ; SI holds the disk retry counter
617                 jz kaboom
618                 pop bx                  ; <I>
619                 pop cx                  ; <H>
620                 pop dx                  ; <G>
621                 pop ax                  ; <F> (AH = 0)
622                 mov al,1                ; Once we fail, only transfer 1 sector
623                 jmp short disk_try_again
624
625 return:         ret
626
627 ;
628 ; getonesec: like getlinsec, but pre-sets the count to 1
629 ;
630 getonesec:
631                 mov bp,1
632                 ; Fall through to getlinsec
633
634 ;
635 ; getlinsec: load a sequence of BP floppy sector given by the linear sector
636 ;            number in DX:AX into the buffer at ES:BX.  We try to optimize
637 ;            by loading up to a whole track at a time, but the user
638 ;            is responsible for not crossing a 64K boundary.
639 ;            (Yes, BP is weird for a count, but it was available...)
640 ;
641 ;            On return, BX points to the first byte after the transferred
642 ;            block.
643 ;
644 ;            The "stupid patch area" gets replaced by the code
645 ;            mov bp,1 ; nop ... (BD 01 00 90 90...) when installing with
646 ;            the -s option.
647 ;
648 ; Stylistic note: use "xchg" instead of "mov" when the source is a register
649 ; that is dead from that point; this saves space.  However, please keep
650 ; the order to dst,src to keep things sane.
651 ;
652 getlinsec:
653                 mov si,[bsSecPerTrack]
654                 ;
655                 ; Dividing by sectors to get (track,sector): we may have
656                 ; up to 2^18 tracks, so we need to do this in two steps
657                 ; to produce a 32-bit quotient.
658                 ;
659                 xchg cx,ax              ; CX <- LSW of LBA
660                 xchg ax,dx
661                 xor dx,dx               ; DX:AX now == MSW of LBA
662                 div si                  ; Obtain MSW of track #
663                 xchg ax,cx              ; Remainder -> MSW of new dividend
664                                         ; LSW of LBA -> LSW of new dividend
665                                         ; Quotient -> MSW of track # 
666                 div si                  ; Obtain LSW of track #, remainder
667                 xchg cx,dx              ; CX <- Sector index (0-based)
668                                         ; DX <- MSW of track #
669                 div word [bsHeads]      ; Convert track to head/cyl
670                 ;
671                 ; Now we have AX = cyl, DX = head, CX = sector (0-based),
672                 ; BP = sectors to transfer, SI = bsSecPerTrack,
673                 ; ES:BX = data target
674                 ;
675 gls_nextchunk:  push si                 ; <A> bsSecPerTrack
676                 push bp                 ; <B> Sectors to transfer
677
678 __BEGIN_STUPID_PATCH_AREA:
679                 sub si,cx               ; Sectors left on track
680                 cmp bp,si
681                 jna gls_lastchunk
682                 mov bp,si               ; No more than a trackful, please!
683 __END_STUPID_PATCH_AREA:
684 gls_lastchunk:  
685                 push ax                 ; <C> Cylinder #
686                 push dx                 ; <D> Head #
687
688                 push cx                 ; <E> Sector #
689                 mov cl,6                ; Because IBM was STOOPID
690                 shl ah,cl               ; and thought 8 bits were enough
691                                         ; then thought 10 bits were enough...
692                 pop cx                  ; <E> Sector #
693                 push cx                 ; <E> Sector #
694                 inc cx                  ; Sector numbers are 1-based
695                 or cl,ah
696                 mov ch,al
697                 mov dh,dl
698                 mov dl,[bsDriveNumber]
699                 xchg ax,bp              ; Sector to transfer count
700                                         ; (xchg shorter than mov)
701                 mov si,retry_count      ; # of times to retry a disk access
702 ;
703 ; Do the disk transfer... save the registers in case we fail :(
704 ;
705 disk_try_again: 
706                 push ax                 ; <F> Number of sectors we're transferring
707                 mov ah,02h              ; READ DISK
708                 push dx                 ; <G>
709                 push cx                 ; <H>
710                 push bx                 ; <I>
711                 push si                 ; <J>
712                 int 13h
713                 pop si                  ; <J>
714                 jc disk_error
715 ;
716 ; Disk access successful
717 ;
718                 pop bx                  ; <I> Buffer location
719                 pop ax                  ; <H> No longer needed
720                 pop ax                  ; <G> No longer needed
721                 pop di                  ; <F> Sector transferred count
722                 pop cx                  ; <E> Sector #
723                 mov ax,di               ; Reduce sector left count
724                 mul word [bsBytesPerSec] ; Figure out how much to advance ptr
725                 add bx,ax               ; Update buffer location
726                 pop dx                  ; <D> Head #
727                 pop ax                  ; <C> Cyl #
728                 pop bp                  ; <B> Sectors left to transfer
729                 pop si                  ; <A> Number of sectors/track
730                 sub bp,di               ; Reduce with # of sectors just read
731                 jz return               ; Done!
732                 add cx,di
733                 cmp cx,si
734                 jb gls_nextchunk
735                 inc dx                  ; Next track on cyl
736                 cmp dx,[bsHeads]        ; Was this the last one?
737                 jb gls_nonewcyl
738                 inc ax                  ; If so, new cylinder
739                 xor dx,dx               ; First head on new cylinder
740 gls_nonewcyl:   sub cx,si               ; First sector on new track
741                 jmp short gls_nextchunk
742
743 bailmsg:        db 'Boot failed', 0Dh, 0Ah, 0
744
745 bs_checkpt      equ $                   ; Must be <= 7DEFh
746
747 bs_checkpt_off  equ ($-$$)
748 %if bs_checkpt_off > 1EFh
749 %error "Boot sector overflow"
750 %endif
751
752                 zb 1EFh-($-$$)
753 bs_magic        equ $                   ; From here to the magic_len equ
754                                         ; must match ldlinux_magic
755 ldlinux_name:   db 'LDLINUX SYS'        ; Looks like this in the root dir
756                 dd HEXDATE              ; Hopefully unique between compiles
757
758 bootsignature   dw 0AA55h
759 magic_len       equ $-bs_magic
760
761 ;
762 ; ===========================================================================
763 ;  End of boot sector
764 ; ===========================================================================
765 ;  Start of LDLINUX.SYS
766 ; ===========================================================================
767
768 ldlinux_sys:
769
770 syslinux_banner db 0Dh, 0Ah, 'SYSLINUX ', version_str, ' ', date, ' ', 0
771                 db 0Dh, 0Ah, 1Ah        ; EOF if we "type" this in DOS
772
773 ldlinux_magic   db 'LDLINUX SYS'
774                 dd HEXDATE
775                 dw 0AA55h
776
777                 align 4
778
779 ldlinux_ent:
780 ;
781 ; Tell the user we got this far
782 ;
783                 mov si,syslinux_banner
784                 call writestr
785 ;
786 ; Remember, the boot sector loaded only the first cluster of LDLINUX.SYS.
787 ; We can really only rely on a single sector having been loaded.  Hence
788 ; we should load the FAT into RAM and start chasing pointers...
789 ;
790                 mov dx,1                        ; 64K
791                 xor ax,ax
792                 div word [bsBytesPerSec]        ; sectors/64K
793                 mov si,ax
794
795                 push es
796                 mov bx,fat_seg                  ; Load into fat_seg:0000
797                 mov es,bx
798                 
799                 mov ax,[bsHidden1]              ; Hidden sectors
800                 mov dx,[bsHidden2]
801                 add ax,[bsResSectors]           ; plus reserved sectors = FAT
802                 adc dx,byte 0
803                 mov cx,[bsFATsecs]              ; Sectors/FAT
804 fat_load_loop:  
805                 mov bp,cx
806                 cmp bp,si
807                 jna fat_load
808                 mov bp,si                       ; A full 64K moby
809 fat_load:       
810                 xor bx,bx                       ; Offset 0 in the current ES
811                 call getlinsecsr
812                 sub cx,bp
813                 jz fat_load_done                ; Last moby?
814                 add ax,bp                       ; Advance sector count
815                 adc dx,byte 0
816                 mov bx,es                       ; Next 64K moby
817                 add bx,1000h
818                 mov es,bx
819                 jmp short fat_load_loop
820 fat_load_done:
821                 pop es
822 ;
823 ; Fine, now we have the FAT in memory.  How big is a cluster, really?
824 ; Also figure out how many clusters will fit in an 8K buffer, and how
825 ; many sectors and bytes that is
826 ;
827                 mov di,[bsBytesPerSec]          ; Used a lot below
828
829                 mov al,[bsSecPerClust]          ; We do this in the boot
830                 xor ah,ah                       ; sector, too, but there
831                 mov [SecPerClust],ax            ; wasn't space to save it
832                 mov si,ax                       ; Also used a lot...
833                 mul di
834                 mov [ClustSize],ax              ; Bytes/cluster
835                 mov bx,ax
836                 mov ax,trackbufsize
837                 xor dx,dx
838                 div bx
839                 mov [BufSafe],ax                ; # of cluster in trackbuf
840                 mul word [SecPerClust]
841                 mov [BufSafeSec],ax
842                 mul di
843                 mov [BufSafeBytes],ax
844                 add ax,getcbuf                  ; Size of getcbuf is the same
845                 mov [EndOfGetCBuf],ax           ; as for trackbuf
846 ;
847 ; FAT12 or FAT16?  This computation is fscking ridiculous...
848 ;
849                 xor dx,dx
850                 xor cx,cx
851                 mov ax,[bsSectors]
852                 and ax,ax
853                 jnz have_secs
854                 mov ax,[bsHugeSectors]
855                 mov dx,[bsHugeSectors+2]
856 have_secs:      sub ax,[bsResSectors]
857                 sbb dx,byte 0
858                 mov cl,[bsFATs]
859 sec_fat_loop:   sub ax,[bsFATsecs]
860                 sbb dx,byte 0
861                 loop sec_fat_loop
862                 push ax
863                 push dx
864                 mov ax,[bsRootDirEnts]
865                 mov bx,32                       ; Smaller than shift since we
866                 mul bx                          ; need the doubleword product
867                 add ax,di
868                 adc dx,byte 0
869                 sub ax,byte 1
870                 sbb dx,byte 0
871                 div di
872                 mov bx,ax
873                 pop dx
874                 pop ax
875                 sub ax,bx
876                 sbb dx,byte 0
877                 div si
878                 cmp ax,4086                     ; Right value?
879                 mov ax,nextcluster_fat16
880                 ja have_fat_type
881 have_fat12:     mov ax,nextcluster_fat12
882 have_fat_type:  mov word [NextCluster],ax
883
884 ;
885 ; Now we read the rest of LDLINUX.SYS.  Don't bother loading the first
886 ; cluster again, though.
887 ;
888 load_rest:
889                 mov cx,[ClustSize]
890                 mov bx,ldlinux_sys
891                 add bx,cx
892                 mov si,[RunLinClust]
893                 call [NextCluster]
894                 xor dx,dx
895                 mov ax,ldlinux_len-1            ; To be on the safe side
896                 add ax,cx
897                 div cx                          ; the number of clusters
898                 dec ax                          ; We've already read one
899                 jz all_read_jmp
900                 mov cx,ax
901                 call getfssec
902 ;
903 ; All loaded up
904 ;
905 all_read_jmp:
906                 jmp all_read
907 ;
908 ; -----------------------------------------------------------------------------
909 ; Subroutines that have to be in the first sector
910 ; -----------------------------------------------------------------------------
911 ;
912 ; getfssec: Get multiple clusters from a file, given the starting cluster.
913 ;
914 ;       This routine makes sure the subtransfers do not cross a 64K boundary,
915 ;       and will correct the situation if it does, UNLESS *sectors* cross
916 ;       64K boundaries.
917 ;
918 ;       ES:BX   -> Buffer
919 ;       SI      -> Starting cluster number (2-based)
920 ;       CX      -> Cluster count (0FFFFh = until end of file)
921 ;
922                                                 ; 386 check
923 getfssec:
924 getfragment:    xor bp,bp                       ; Fragment sector count
925                 mov ax,si                       ; Get sector address
926                 dec ax                          ; Convert to 0-based
927                 dec ax
928                 mul word [SecPerClust]
929                 add ax,[DataArea1]
930                 adc dx,[DataArea2]
931 getseccnt:                                      ; See if we can read > 1 clust
932                 add bp,[SecPerClust]
933                 dec cx                          ; Reduce clusters left to find
934                 mov di,si                       ; Predict next cluster
935                 inc di
936                 call [NextCluster]
937                 jc gfs_eof                      ; At EOF?
938                 jcxz endfragment                ; Or was it the last we wanted?
939                 cmp si,di                       ; Is file continuous?
940                 jz getseccnt                    ; Yes, we can get
941 endfragment:    clc                             ; Not at EOF
942 gfs_eof:        pushf                           ; Remember EOF or not
943                 push si
944                 push cx
945 gfs_getchunk:
946                 push ax
947                 push dx
948                 mov ax,es                       ; Check for 64K boundaries.
949                 mov cl,4
950                 shl ax,cl
951                 add ax,bx
952                 xor dx,dx
953                 neg ax
954                 jnz gfs_partseg
955                 inc dx                          ; Full 64K segment
956 gfs_partseg:
957                 div word [bsBytesPerSec]        ; How many sectors fit?
958                 mov si,bp
959                 sub si,ax                       ; Compute remaining sectors
960                 jbe gfs_lastchunk
961                 mov bp,ax
962                 pop dx
963                 pop ax
964                 call getlinsecsr
965                 add ax,bp
966                 adc dx,byte 0
967                 mov bp,si                       ; Remaining sector count
968                 jmp short gfs_getchunk
969 gfs_lastchunk:  pop dx
970                 pop ax          
971                 call getlinsec
972                 pop cx
973                 pop si
974                 popf
975                 jcxz gfs_return                 ; If we hit the count limit
976                 jnc getfragment                 ; If we didn't hit EOF
977 gfs_return:     ret
978
979 ;
980 ; getlinsecsr: save registers, call getlinsec, restore registers
981 ;
982 getlinsecsr:    push ax
983                 push dx
984                 push cx
985                 push bp
986                 push si
987                 push di
988                 call getlinsec
989                 pop di
990                 pop si
991                 pop bp
992                 pop cx
993                 pop dx
994                 pop ax
995                 ret
996
997 ;
998 ; nextcluster: Advance a cluster pointer in SI to the next cluster
999 ;              pointed at in the FAT tables (note: FAT12 assumed)
1000 ;              Sets CF on return if end of file.
1001 ;
1002 ;              The variable NextCluster gets set to the appropriate
1003 ;              value here.
1004 ;
1005 nextcluster_fat12:
1006                 push ax
1007                 push ds
1008                 mov ax,fat_seg
1009                 mov ds,ax
1010                 mov ax,si                       ; Multiply by 3/2
1011                 shr ax,1
1012                 pushf                           ; CF now set if odd
1013                 add si,ax
1014                 mov si,[si]
1015                 popf
1016                 jnc nc_even
1017                 shr si,1                        ; Needed for odd only
1018                 shr si,1
1019                 shr si,1
1020                 shr si,1
1021 nc_even:
1022                 and si,0FFFh
1023                 cmp si,0FF0h                    ; Clears CF if at end of file
1024                 cmc                             ; But we want it SET...
1025                 pop ds
1026                 pop ax
1027 nc_return:      ret
1028
1029 ;
1030 ; FAT16 decoding routine.  Note that a 16-bit FAT can be up to 128K,
1031 ; so we have to decide if we're in the "low" or the "high" 64K-segment...
1032 ;
1033 nextcluster_fat16:
1034                 push ax
1035                 push ds
1036                 mov ax,fat_seg
1037                 shl si,1
1038                 jnc .seg0
1039                 mov ax,fat_seg+1000h
1040 .seg0:          mov ds,ax
1041                 mov si,[si]
1042                 cmp si,0FFF0h
1043                 cmc
1044                 pop ds
1045                 pop ax
1046                 ret
1047 ;
1048 ; Debug routine
1049 ;
1050 %ifdef debug
1051 safedumpregs:
1052                 cmp word [Debug_Magic],0D00Dh
1053                 jnz nc_return
1054                 jmp dumpregs
1055 %endif
1056
1057 rl_checkpt      equ $                           ; Must be <= 8000h
1058
1059 rl_checkpt_off  equ ($-$$)
1060 %if rl_checkpt_off > 400h
1061 %error "Sector 1 overflow"
1062 %endif
1063
1064 ; ----------------------------------------------------------------------------
1065 ;  End of code and data that have to be in the first sector
1066 ; ----------------------------------------------------------------------------
1067
1068 all_read:
1069 ;
1070 ; Let the user (and programmer!) know we got this far.  This used to be
1071 ; in Sector 1, but makes a lot more sense here.
1072 ;
1073                 mov si,copyright_str
1074                 call writestr
1075 ;
1076 ; Check that no moron is trying to boot Linux on a 286 or so.  According
1077 ; to Intel, the way to check is to see if the high 4 bits of the FLAGS
1078 ; register are either all stuck at 1 (8086/8088) or all stuck at 0
1079 ; (286 in real mode), if not it is a 386 or higher.  They didn't
1080 ; say how to check for a 186/188, so I *hope* it falls out as a 8086
1081 ; or 286 in this test.
1082 ;
1083 ; Also, provide an escape route in case it doesn't work.
1084 ;
1085 check_escapes:
1086                 mov ah,02h                      ; Check keyboard flags
1087                 int 16h
1088                 mov [KbdFlags],al               ; Save for boot prompt check
1089                 test al,04h                     ; Ctrl->skip 386 check
1090                 jnz skip_checks
1091 test_8086:
1092                 pushf                           ; Get flags
1093                 pop ax
1094                 and ax,0FFFh                    ; Clear top 4 bits
1095                 push ax                         ; Load into FLAGS
1096                 popf
1097                 pushf                           ; And load back
1098                 pop ax
1099                 and ax,0F000h                   ; Get top 4 bits
1100                 cmp ax,0F000h                   ; If set -> 8086/8088
1101                 je not_386
1102 test_286:
1103                 pushf                           ; Get flags
1104                 pop ax
1105                 or ax,0F000h                    ; Set top 4 bits
1106                 push ax
1107                 popf
1108                 pushf
1109                 pop ax
1110                 and ax,0F000h                   ; Get top 4 bits
1111                 jnz is_386                      ; If not clear -> 386
1112 not_386:
1113                 mov si,err_not386
1114                 call writestr
1115                 jmp kaboom
1116 is_386:
1117                 ; Now we know it's a 386 or higher
1118 ;
1119 ; Now check that there is sufficient low (DOS) memory
1120 ;
1121                 int 12h
1122                 cmp ax,(real_mode_seg+0xa00) >> 6
1123                 jae enough_ram
1124                 mov si,err_noram
1125                 call writestr
1126                 jmp kaboom
1127 enough_ram:
1128 skip_checks:
1129 ;
1130 ; Check if we're 386 (as opposed to 486+); if so we need to blank out
1131 ; the WBINVD instruction
1132 ;
1133 ; We check for 486 by setting EFLAGS.AC
1134 ;
1135                 pushfd                          ; Save the good flags
1136                 pushfd
1137                 pop eax
1138                 mov ebx,eax
1139                 xor eax,(1 << 18)               ; AC bit
1140                 push eax
1141                 popfd
1142                 pushfd
1143                 pop eax
1144                 popfd                           ; Restore the original flags
1145                 xor eax,ebx
1146                 jnz is_486
1147 ;
1148 ; 386 - Looks like we better blot out the WBINVD instruction
1149 ;
1150                 mov byte [try_wbinvd],0c3h              ; Near RET              
1151 is_486:
1152
1153 ;
1154 ; Initialization that does not need to go into the any of the pre-load
1155 ; areas
1156 ;
1157                 ; Now set up screen parameters
1158                 call adjust_screen
1159 ;
1160 ; Now, everything is "up and running"... patch kaboom for more
1161 ; verbosity and using the full screen system
1162 ;
1163                 mov byte [kaboom.patch],0e9h            ; JMP NEAR
1164                 mov word [kaboom.patch+1],kaboom2-(kaboom.patch+3)
1165
1166 ;
1167 ; Now we're all set to start with our *real* business.  First load the
1168 ; configuration file (if any) and parse it.
1169 ;
1170 ; In previous versions I avoided using 32-bit registers because of a
1171 ; rumour some BIOSes clobbered the upper half of 32-bit registers at
1172 ; random.  I figure, though, that if there are any of those still left
1173 ; they probably won't be trying to install Linux on them...
1174 ;
1175 ; The code is still ripe with 16-bitisms, though.  Not worth the hassle
1176 ; to take'm out.  In fact, we may want to put them back if we're going
1177 ; to boot ELKS at some point.
1178 ;
1179                 mov si,linuxauto_cmd            ; Default command: "linux auto"
1180                 mov di,default_cmd
1181                 mov cx,linuxauto_len
1182                 rep movsb
1183
1184                 mov di,KbdMap                   ; Default keymap 1:1
1185                 xor al,al
1186                 mov cx,256
1187 mkkeymap:       stosb
1188                 inc al
1189                 loop mkkeymap
1190
1191 ;
1192 ; Load configuration file
1193 ;
1194                 mov di,syslinux_cfg
1195                 call open
1196                 jz near no_config_file
1197 parse_config:
1198                 call getkeyword
1199                 jc near end_config_file         ; Config file loaded
1200                 cmp ax,'de'                     ; DEfault
1201                 je pc_default
1202                 cmp ax,'ap'                     ; APpend
1203                 je pc_append
1204                 cmp ax,'ti'                     ; TImeout
1205                 je near pc_timeout
1206                 cmp ax,'pr'                     ; PRompt
1207                 je near pc_prompt
1208                 cmp ax,'fo'                     ; FOnt
1209                 je near pc_font
1210                 cmp ax,'kb'                     ; KBd
1211                 je near pc_kbd
1212                 cmp ax,'di'                     ; DIsplay
1213                 je near pc_display
1214                 cmp ax,'la'                     ; LAbel
1215                 je near pc_label
1216                 cmp ax,'ke'                     ; KErnel
1217                 je pc_kernel
1218                 cmp ax,'im'                     ; IMplicit
1219                 je near pc_implicit
1220                 cmp ax,'se'                     ; SErial
1221                 je near pc_serial
1222                 cmp al,'f'                      ; F-key
1223                 jne parse_config
1224                 jmp pc_fkey
1225
1226 pc_default:     mov di,default_cmd              ; "default" command
1227                 call getline
1228                 xor al,al
1229                 stosb                           ; null-terminate
1230                 jmp short parse_config
1231
1232 pc_append:      cmp word [VKernelCtr],byte 0    ; "append" command
1233                 ja pc_append_vk
1234                 mov di,AppendBuf
1235                 call getline
1236                 sub di,AppendBuf
1237 pc_app1:        mov [AppendLen],di
1238                 jmp short parse_config
1239 pc_append_vk:   mov di,VKernelBuf+vk_append     ; "append" command (vkernel)
1240                 call getline
1241                 sub di,VKernelBuf+vk_append
1242                 cmp di,byte 2
1243                 jne pc_app2
1244                 cmp byte [VKernelBuf+vk_append],'-'
1245                 jne pc_app2
1246                 mov di,0                        ; If "append -" -> null string
1247 pc_app2:        mov [VKernelBuf+vk_appendlen],di
1248                 jmp short parse_config_2        
1249
1250 pc_kernel:      cmp word [VKernelCtr],byte 0    ; "kernel" command
1251                 je near parse_config            ; ("label" section only)
1252                 mov di,trackbuf
1253                 push di
1254                 call getline
1255                 pop si
1256                 mov di,VKernelBuf+vk_rname
1257                 call mangle_name
1258                 jmp short parse_config_2
1259
1260 pc_timeout:     call getint                     ; "timeout" command
1261                 jc parse_config_2
1262                 mov ax,0D215h                   ; There are approx 1.D215h
1263                 mul bx                          ; clock ticks per 1/10 s
1264                 add bx,dx
1265                 mov [KbdTimeOut],bx
1266                 jmp short parse_config_2
1267
1268 pc_display:     call pc_getfile                 ; "display" command
1269                 jz parse_config_2               ; File not found?
1270                 call get_msg_file               ; Load and display file
1271 parse_config_2: jmp parse_config
1272
1273 pc_prompt:      call getint                     ; "prompt" command
1274                 jc parse_config_2
1275                 mov [ForcePrompt],bx
1276                 jmp short parse_config_2
1277
1278 pc_implicit:    call getint                     ; "implicit" command
1279                 jc parse_config_2
1280                 mov [AllowImplicit],bx
1281                 jmp short parse_config_2
1282
1283 pc_serial:      call getint                     ; "serial" command
1284                 jc parse_config_2
1285                 push bx                         ; Serial port #
1286                 call skipspace
1287                 jc parse_config_2
1288                 call ungetc
1289                 call getint
1290                 mov [FlowControl], word 0       ; Default to no flow control
1291                 jc .nobaud
1292 .valid_baud:    
1293                 push ebx
1294                 call skipspace
1295                 jc .no_flow
1296                 call ungetc
1297                 call getint                     ; Hardware flow control?
1298                 jnc .valid_flow
1299 .no_flow:
1300                 xor bx,bx                       ; Default -> no flow control
1301 .valid_flow:
1302                 and bh,0Fh                      ; FlowIgnore
1303                 shl bh,4
1304                 mov [FlowIgnore],bh
1305                 mov bh,bl
1306                 and bx,0F003h                   ; Valid bits
1307                 mov [FlowControl],bx
1308                 pop ebx                         ; Baud rate
1309                 jmp short .parse_baud
1310 .nobaud:
1311                 mov ebx,DEFAULT_BAUD            ; No baud rate given
1312 .parse_baud:
1313                 pop di                          ; Serial port #
1314                 cmp ebx,byte 75
1315                 jb parse_config_2               ; < 75 baud == bogus
1316                 mov eax,BAUD_DIVISOR
1317                 cdq
1318                 div ebx
1319                 push ax                         ; Baud rate divisor
1320                 cmp di,3
1321                 ja .port_is_io                  ; If port > 3 then port is I/O addr
1322                 shl di,1
1323                 mov di,[di+serial_base]         ; Get the I/O port from the BIOS
1324 .port_is_io:
1325                 mov [SerialPort],di
1326                 lea dx,[di+3]                   ; DX -> LCR
1327                 mov al,83h                      ; Enable DLAB
1328                 call slow_out
1329                 pop ax                          ; Divisor
1330                 mov dx,di                       ; DX -> LS
1331                 call slow_out
1332                 inc dx                          ; DX -> MS
1333                 mov al,ah
1334                 call slow_out
1335                 mov al,03h                      ; Disable DLAB
1336                 add dx,byte 2                   ; DX -> LCR
1337                 call slow_out
1338                 in al,dx                        ; Read back LCR (detect missing hw)
1339                 cmp al,03h                      ; If nothing here we'll read 00 or FF
1340                 jne .serial_port_bad            ; Assume serial port busted
1341                 sub dx,byte 2                   ; DX -> IER
1342                 xor al,al                       ; IRQ disable
1343                 call slow_out
1344
1345                 add dx,byte 3                   ; DX -> MCR
1346                 in al,dx
1347                 or al,[FlowOutput]              ; Assert bits
1348                 call slow_out
1349
1350                 ; Show some life
1351                 mov si,syslinux_banner
1352                 call write_serial_str
1353                 mov si,copyright_str
1354                 call write_serial_str
1355
1356                 jmp short parse_config_3
1357
1358 .serial_port_bad:
1359                 mov [SerialPort], word 0
1360                 jmp short parse_config_3
1361
1362 pc_fkey:        sub ah,'1'
1363                 jnb pc_fkey1
1364                 mov ah,9                        ; F10
1365 pc_fkey1:       xor cx,cx
1366                 mov cl,ah
1367                 push cx
1368                 mov ax,1
1369                 shl ax,cl
1370                 or [FKeyMap], ax                ; Mark that we have this loaded
1371                 mov di,trackbuf
1372                 push di
1373                 call getline                    ; Get filename to display
1374                 pop si
1375                 pop di
1376                 shl di,4                        ; Multiply number by 16
1377                 add di,FKeyName
1378                 call mangle_name                ; Mangle file name
1379                 jmp short parse_config_3
1380
1381 pc_label:       call commit_vk                  ; Commit any current vkernel
1382                 mov di,trackbuf                 ; Get virtual filename
1383                 push di
1384                 call getline
1385                 pop si
1386                 mov di,VKernelBuf+vk_vname
1387                 call mangle_name                ; Mangle virtual name
1388                 inc word [VKernelCtr]           ; One more vkernel
1389                 mov si,VKernelBuf+vk_vname      ; By default, rname == vname
1390                 mov di,VKernelBuf+vk_rname
1391                 mov cx,11
1392                 rep movsb
1393                 mov si,AppendBuf                ; Default append==global append
1394                 mov di,VKernelBuf+vk_append
1395                 mov cx,[AppendLen]
1396                 mov [VKernelBuf+vk_appendlen],cx
1397                 rep movsb
1398                 jmp near parse_config_3
1399
1400 pc_font:        call pc_getfile                 ; "font" command
1401                 jz parse_config_3               ; File not found?
1402                 call loadfont                   ; Load and install font
1403                 jmp short parse_config_3
1404
1405 pc_kbd:         call pc_getfile                 ; "kbd" command
1406                 jz parse_config_3
1407                 call loadkeys
1408 parse_config_3: jmp parse_config
1409
1410 ;
1411 ; pc_getfile:   For command line options that take file argument, this
1412 ;               routine decodes the file argument and runs it through searchdir
1413 ;
1414 pc_getfile:     mov di,trackbuf
1415                 push di
1416                 call getline
1417                 pop si
1418                 mov di,MNameBuf
1419                 push di
1420                 call mangle_name
1421                 pop di
1422                 jmp searchdir                   ; Tailcall
1423
1424 ;
1425 ; commit_vk: Store the current VKernelBuf into buffer segment
1426 ;
1427 commit_vk:
1428                 cmp word [VKernelCtr],byte 0
1429                 je cvk_ret                      ; No VKernel = return
1430                 cmp word [VKernelCtr],max_vk    ; Above limit?
1431                 ja cvk_overflow
1432                 mov di,[VKernelCtr]
1433                 dec di
1434                 shl di,vk_shift
1435                 mov si,VKernelBuf
1436                 mov cx,(vk_size >> 2)
1437                 push es
1438                 push word vk_seg
1439                 pop es
1440                 rep movsd                       ; Copy to buffer segment
1441                 pop es
1442 cvk_ret:        ret
1443 cvk_overflow:   mov word [VKernelCtr],max_vk    ; No more than max_vk, please
1444                 ret
1445
1446 ;
1447 ; End of configuration file
1448 ;
1449 end_config_file:
1450                 call commit_vk                  ; Commit any current vkernel
1451 no_config_file:
1452 ;
1453 ; Check whether or not we are supposed to display the boot prompt.
1454 ;
1455 check_for_key:
1456                 cmp word [ForcePrompt],byte 0   ; Force prompt?
1457                 jnz enter_command
1458                 test byte [KbdFlags],5Bh        ; Caps, Scroll, Shift, Alt
1459                 jz near auto_boot               ; If neither, default boot
1460
1461 enter_command:
1462                 mov si,boot_prompt
1463                 call cwritestr
1464
1465                 mov byte [FuncFlag],0           ; <Ctrl-F> not pressed
1466                 mov di,command_line
1467 ;
1468 ; get the very first character -- we can either time
1469 ; out, or receive a character press at this time.  Some dorky BIOSes stuff
1470 ; a return in the buffer on bootup, so wipe the keyboard buffer first.
1471 ;
1472 clear_buffer:   mov ah,1                        ; Check for pending char
1473                 int 16h
1474                 jz get_char_time
1475                 xor ax,ax                       ; Get char
1476                 int 16h
1477                 jmp short clear_buffer
1478 get_char_time:  
1479                 call vgashowcursor
1480                 mov cx,[KbdTimeOut]
1481                 and cx,cx
1482                 jz get_char                     ; Timeout == 0 -> no timeout
1483                 inc cx                          ; The first loop will happen
1484                                                 ; immediately as we don't
1485                                                 ; know the appropriate DX value
1486 time_loop:      push cx
1487 tick_loop:      push dx
1488                 call pollchar
1489                 jnz get_char_pop
1490                 xor ax,ax
1491                 int 1Ah                         ; Get time "of day"
1492                 pop ax
1493                 cmp dx,ax                       ; Has the timer advanced?
1494                 je tick_loop
1495                 pop cx
1496                 loop time_loop                  ; If so, decrement counter
1497                 call vgahidecursor
1498                 jmp command_done                ; Timeout!
1499
1500 get_char_pop:   pop eax                         ; Clear stack
1501 get_char:
1502                 call vgashowcursor
1503                 call getchar
1504                 call vgahidecursor
1505                 and al,al
1506                 jz func_key
1507
1508 got_ascii:      cmp al,7Fh                      ; <DEL> == <BS>
1509                 je backspace
1510                 cmp al,' '                      ; ASCII?
1511                 jb not_ascii
1512                 ja enter_char
1513                 cmp di,command_line             ; Space must not be first
1514                 je get_char
1515 enter_char:     test byte [FuncFlag],1
1516                 jz .not_ctrl_f
1517                 mov byte [FuncFlag],0
1518                 cmp al,'0'
1519                 jb .not_ctrl_f
1520                 je ctrl_f_0
1521                 cmp al,'9'
1522                 jbe ctrl_f
1523 .not_ctrl_f:    cmp di,max_cmd_len+command_line ; Check there's space
1524                 jnb get_char
1525                 stosb                           ; Save it
1526                 call writechr                   ; Echo to screen
1527 get_char_2:     jmp short get_char
1528 not_ascii:      mov byte [FuncFlag],0
1529                 cmp al,0Dh                      ; Enter
1530                 je command_done
1531                 cmp al,06h                      ; <Ctrl-F>
1532                 je set_func_flag
1533                 cmp al,08h                      ; Backspace
1534                 jne get_char
1535 backspace:      cmp di,command_line             ; Make sure there is anything
1536                 je get_char                     ; to erase
1537                 dec di                          ; Unstore one character
1538                 mov si,wipe_char                ; and erase it from the screen
1539                 call cwritestr
1540                 jmp short get_char_2
1541
1542 set_func_flag:
1543                 mov byte [FuncFlag],1
1544                 jmp short get_char_2
1545
1546 ctrl_f_0:       add al,10                       ; <Ctrl-F>0 == F10
1547 ctrl_f:         push di
1548                 sub al,'1'
1549                 xor ah,ah
1550                 jmp short show_help
1551
1552 func_key:
1553                 push di
1554                 cmp ah,68                       ; F10
1555                 ja get_char_2
1556                 sub ah,59                       ; F1
1557                 jb get_char_2
1558                 shr ax,8
1559 show_help:      ; AX = func key # (0 = F1, 9 = F10)
1560                 mov cl,al
1561                 shl ax,4                        ; Convert to x16
1562                 mov bx,1
1563                 shl bx,cl
1564                 and bx,[FKeyMap]
1565                 jz get_char_2                   ; Undefined F-key
1566                 mov di,ax
1567                 add di,FKeyName
1568                 call searchdir
1569                 jz fk_nofile
1570                 push si
1571                 call crlf
1572                 pop si
1573                 call get_msg_file
1574                 jmp short fk_wrcmd
1575 fk_nofile:
1576                 call crlf
1577 fk_wrcmd:
1578                 mov si,boot_prompt
1579                 call cwritestr
1580                 pop di                          ; Command line write pointer
1581                 push di
1582                 mov byte [di],0                 ; Null-terminate command line
1583                 mov si,command_line
1584                 call cwritestr                  ; Write command line so far
1585                 pop di
1586                 jmp short get_char_2
1587 auto_boot:
1588                 mov si,default_cmd
1589                 mov di,command_line
1590                 mov cx,(max_cmd_len+4) >> 2
1591                 rep movsd
1592                 jmp short load_kernel
1593 command_done:
1594                 call crlf
1595                 cmp di,command_line             ; Did we just hit return?
1596                 je auto_boot
1597                 xor al,al                       ; Store a final null
1598                 stosb
1599
1600 load_kernel:                                    ; Load the kernel now
1601 ;
1602 ; First we need to mangle the kernel name the way DOS would...
1603 ;
1604                 mov si,command_line
1605                 mov di,KernelName
1606                 push si
1607                 push di
1608                 call mangle_name
1609                 pop di
1610                 pop si
1611 ;
1612 ; Fast-forward to first option (we start over from the beginning, since
1613 ; mangle_name doesn't necessarily return a consistent ending state.)
1614 ;
1615 clin_non_wsp:   lodsb
1616                 cmp al,' '
1617                 ja clin_non_wsp
1618 clin_is_wsp:    and al,al
1619                 jz clin_opt_ptr
1620                 lodsb
1621                 cmp al,' '
1622                 jbe clin_is_wsp
1623 clin_opt_ptr:   dec si                          ; Point to first nonblank
1624                 mov [CmdOptPtr],si              ; Save ptr to first option
1625 ;
1626 ; Now check if it is a "virtual kernel"
1627 ;
1628                 mov cx,[VKernelCtr]
1629                 push ds
1630                 push word vk_seg
1631                 pop ds
1632                 cmp cx,byte 0
1633                 je not_vk
1634                 xor si,si                       ; Point to first vkernel
1635 vk_check:       pusha
1636                 mov cx,11
1637                 repe cmpsb                      ; Is this it?
1638                 je near vk_found
1639                 popa
1640                 add si,vk_size
1641                 loop vk_check
1642 not_vk:         pop ds
1643 ;
1644 ; Not a "virtual kernel" - check that's OK and construct the command line
1645 ;
1646                 cmp word [AllowImplicit],byte 0
1647                 je bad_implicit
1648                 push es
1649                 push si
1650                 push di
1651                 mov di,real_mode_seg
1652                 mov es,di
1653                 mov si,AppendBuf
1654                 mov di,cmd_line_here
1655                 mov cx,[AppendLen]
1656                 rep movsb
1657                 mov [CmdLinePtr],di
1658                 pop di
1659                 pop si
1660                 pop es
1661                 mov bx,exten_count << 2         ; Alternates to try
1662 ;
1663 ; Find the kernel on disk
1664 ;
1665 get_kernel:     mov byte [KernelName+11],0      ; Zero-terminate filename/extension
1666                 mov eax,[KernelName+8]          ; Save initial extension
1667                 mov [OrigKernelExt],eax
1668 .search_loop:   push bx
1669                 mov di,KernelName               ; Search on disk
1670                 call searchdir
1671                 pop bx
1672                 jnz kernel_good
1673                 mov eax,[exten_table+bx]        ; Try a different extension
1674                 mov [KernelName+8],eax
1675                 sub bx,byte 4
1676                 jnb .search_loop
1677 bad_kernel:     
1678                 mov si,KernelName
1679                 mov di,KernelCName
1680                 push di
1681                 call unmangle_name              ; Get human form
1682                 mov si,err_notfound             ; Complain about missing kernel
1683                 call cwritestr
1684                 pop si                          ; KernelCName
1685                 call cwritestr
1686                 mov si,crlf_msg
1687                 jmp abort_load                  ; Ask user for clue
1688 ;
1689 ; bad_implicit: The user entered a nonvirtual kernel name, with "implicit 0"
1690 ;
1691 bad_implicit:   mov si,KernelName               ; For the error message
1692                 mov di,KernelCName
1693                 call unmangle_name
1694                 jmp short bad_kernel
1695 ;
1696 ; vk_found: We *are* using a "virtual kernel"
1697 ;
1698 vk_found:       popa
1699                 push di
1700                 mov di,VKernelBuf
1701                 mov cx,vk_size >> 2
1702                 rep movsd
1703                 push es                         ; Restore old DS
1704                 pop ds
1705                 push es
1706                 push word real_mode_seg
1707                 pop es
1708                 mov di,cmd_line_here
1709                 mov si,VKernelBuf+vk_append
1710                 mov cx,[VKernelBuf+vk_appendlen]
1711                 rep movsb
1712                 mov [CmdLinePtr],di             ; Where to add rest of cmd
1713                 pop es
1714                 pop di                          ; DI -> KernelName
1715                 push di 
1716                 mov si,VKernelBuf+vk_rname
1717                 mov cx,11                       ; We need ECX == CX later
1718                 rep movsb
1719                 pop di
1720                 xor bx,bx                       ; Try only one version
1721                 jmp get_kernel
1722 ;
1723 ; kernel_corrupt: Called if the kernel file does not seem healthy
1724 ;
1725 kernel_corrupt: mov si,err_notkernel
1726                 jmp abort_load
1727 ;
1728 ; This is it!  We have a name (and location on the disk)... let's load
1729 ; that sucker!!  First we have to decide what kind of file this is; base
1730 ; that decision on the file extension.  The following extensions are
1731 ; recognized:
1732 ;
1733 ; .COM  - COMBOOT image
1734 ; .CBT  - COMBOOT image
1735 ; .BS   - Boot sector
1736 ; .BSS  - Boot sector, but transfer over DOS superblock
1737 ;
1738 ; Anything else is assumed to be a Linux kernel.
1739 ;
1740 kernel_good:
1741                 pusha
1742                 mov si,KernelName
1743                 mov di,KernelCName
1744                 call unmangle_name              ; Get human form
1745                 sub di,KernelCName
1746                 mov [KernelCNameLen],di
1747                 popa
1748
1749                 mov ecx,[KernelName+8]          ; Get (mangled) extension
1750                 cmp ecx,'COM'
1751                 je near is_comboot_image
1752                 cmp ecx,'CBT'
1753                 je near is_comboot_image
1754                 cmp ecx,'BS '
1755                 je near is_bootsector
1756                 cmp ecx,'BSS'
1757                 je near is_bss_sector
1758                 ; Otherwise Linux kernel
1759 ;
1760 ; A Linux kernel consists of three parts: boot sector, setup code, and
1761 ; kernel code.  The boot sector is never executed when using an external
1762 ; booting utility, but it contains some status bytes that are necessary.
1763 ; The boot sector and setup code together form exactly 5 sectors that
1764 ; should be loaded at 9000:0.  The subsequent code should be loaded
1765 ; at 1000:0.  For simplicity, we load the whole thing at 0F60:0, and
1766 ; copy the latter stuff afterwards.
1767 ;
1768 ; NOTE: In the previous code I have avoided making any assumptions regarding
1769 ; the size of a sector, in case this concept ever gets extended to other
1770 ; media like CD-ROM (not that a CD-ROM would be bloody likely to use a FAT
1771 ; filesystem, of course).  However, a "sector" when it comes to Linux booting
1772 ; stuff means 512 bytes *no matter what*, so here I am using that piece
1773 ; of knowledge.
1774 ;
1775 ; First check that our kernel is at least 1K and less than 8M (if it is
1776 ; more than 8M, we need to change the logic for loading it anyway...)
1777 ;
1778 ; We used to require the kernel to be 64K or larger, but it has gotten
1779 ; popular to use the Linux kernel format for other things, which may
1780 ; not be so large.
1781 ;
1782 is_linux_kernel:
1783                 cmp dx,80h                      ; 8 megs
1784                 ja kernel_corrupt
1785                 and dx,dx
1786                 jnz kernel_sane
1787                 cmp ax,1024                     ; Bootsect + 1 setup sect
1788                 jb kernel_corrupt
1789 kernel_sane:    push ax
1790                 push dx
1791                 push si
1792                 mov si,loading_msg
1793                 call cwritestr
1794 ;
1795 ; Now start transferring the kernel
1796 ;
1797                 push word real_mode_seg
1798                 pop es
1799
1800                 push ax
1801                 push dx
1802                 div word [ClustSize]            ; # of clusters total
1803                 and dx,dx                       ; Round up
1804                 setnz dl
1805                 movzx dx,dl
1806                 add ax,dx
1807                 mov [KernelClust],ax
1808                 pop dx
1809                 pop ax
1810                 mov [KernelSize],ax
1811                 mov [KernelSize+2],dx
1812 ;
1813 ; Now, if we transfer these straight, we'll hit 64K boundaries.  Hence we
1814 ; have to see if we're loading more than 64K, and if so, load it step by
1815 ; step.
1816 ;
1817                 mov dx,1                        ; 10000h
1818                 xor ax,ax
1819                 div word [ClustSize]
1820                 mov [ClustPerMoby],ax           ; Clusters/64K
1821 ;
1822 ; Start by loading the bootsector/setup code, to see if we need to
1823 ; do something funky.  It should fit in the first 32K (loading 64K won't
1824 ; work since we might have funny stuff up near the end of memory).
1825 ; If we have larger than 32K clusters, yes, we're hosed.
1826 ;
1827                 call abort_check                ; Check for abort key
1828                 mov cx,[ClustPerMoby]
1829                 shr cx,1                        ; Half a moby
1830                 cmp cx,[KernelClust]
1831                 jna .normalkernel
1832                 mov cx,[KernelClust]
1833 .normalkernel:
1834                 sub [KernelClust],cx
1835                 xor bx,bx
1836                 pop si                          ; Cluster pointer on stack
1837                 call getfssec
1838                 cmp word [es:bs_bootsign],0AA55h
1839                 jne near kernel_corrupt         ; Boot sec signature missing
1840 ;
1841 ; Get the BIOS' idea of what the size of high memory is.
1842 ;
1843                 push si                         ; Save our cluster pointer!
1844 ;
1845 ; First, try INT 15:E820 (get BIOS memory map)
1846 ;
1847 get_e820:
1848                 push es
1849                 xor ebx,ebx                     ; Start with first record
1850                 mov es,bx                       ; Need ES = DS = 0 for now
1851                 jmp short .do_e820              ; Skip "at end" check first time!
1852 .int_loop:      and ebx,ebx                     ; If we're back at beginning...
1853                 jz no_e820                      ; ... bail; nothing found
1854 .do_e820:       mov eax,0000E820h
1855                 mov edx,534D4150h               ; "SMAP" backwards
1856                 mov ecx,20
1857                 mov di,E820Buf
1858                 int 15h
1859                 jc no_e820
1860                 cmp eax,534D4150h
1861                 jne no_e820
1862 ;
1863 ; Look for a memory block starting at <= 1 MB and continuing upward
1864 ;
1865                 cmp dword [E820Buf+4], byte 0
1866                 ja .int_loop                    ; Start >= 4 GB?
1867                 mov edx, (1 << 20)
1868                 sub edx, [E820Buf]
1869                 jb .int_loop                    ; Start >= 1 MB?
1870                 mov eax, 0FFFFFFFFh
1871                 cmp dword [E820Buf+12], byte 0
1872                 ja .huge                        ; Size >= 4 GB
1873                 mov eax, [E820Buf+8]
1874 .huge:          sub eax, edx                    ; Adjust size to start at 1 MB
1875                 jbe .int_loop                   ; Completely below 1 MB?
1876
1877                 ; Now EAX contains the size of memory 1 MB...up
1878                 cmp dword [E820Buf+16], byte 1
1879                 jne near err_nohighmem          ; High memory isn't usable memory!!!!
1880
1881                 ; We're good!
1882                 pop es
1883                 jmp short got_highmem_add1mb    ; Still need to add low 1 MB
1884
1885 ;
1886 ; INT 15:E820 failed.  Try INT 15:E801.
1887 ;
1888 no_e820:        pop es
1889
1890                 mov ax,0e801h                   ; Query high memory (semi-recent)
1891                 int 15h
1892                 jc no_e801
1893                 cmp ax,3c00h
1894                 ja no_e801                      ; > 3C00h something's wrong with this call
1895                 jb e801_hole                    ; If memory hole we can only use low part
1896
1897                 mov ax,bx
1898                 shl eax,16                      ; 64K chunks
1899                 add eax,(16 << 20)              ; Add first 16M
1900                 jmp short got_highmem                           
1901
1902 ;
1903 ; INT 15:E801 failed.  Try INT 15:88.
1904 ;
1905 no_e801:
1906                 mov ah,88h                      ; Query high memory (oldest)
1907                 int 15h
1908                 cmp ax,14*1024                  ; Don't trust memory >15M
1909                 jna e801_hole
1910                 mov ax,14*1024
1911 e801_hole:
1912                 and eax,0ffffh
1913                 shl eax,10                      ; Convert from kilobytes
1914 got_highmem_add1mb:
1915                 add eax,(1 << 20)               ; First megabyte
1916 got_highmem:
1917                 mov [HighMemSize],eax
1918
1919 ;
1920 ; Construct the command line (append options have already been copied)
1921 ;
1922                 mov di,[CmdLinePtr]
1923                 mov si,boot_image               ; BOOT_IMAGE=
1924                 mov cx,boot_image_len
1925                 rep movsb
1926                 mov si,KernelCName              ; Unmangled kernel name
1927                 mov cx,[KernelCNameLen]
1928                 rep movsb
1929                 mov al,' '                      ; Space
1930                 stosb
1931                 mov si,[CmdOptPtr]              ; Options from user input
1932                 mov cx,(kern_cmd_len+3) >> 2
1933                 rep movsd
1934 ;
1935 %ifdef debug
1936                 push ds                         ; DEBUG DEBUG DEBUG
1937                 push es
1938                 pop ds
1939                 mov si,cmd_line_here
1940                 call cwritestr
1941                 pop ds
1942                 call crlf
1943 %endif
1944 ;
1945 ; Scan through the command line for anything that looks like we might be
1946 ; interested in.  The original version of this code automatically assumed
1947 ; the first option was BOOT_IMAGE=, but that is no longer certain.
1948 ;
1949                 mov si,cmd_line_here
1950                 mov byte [initrd_flag],0
1951                 push es                         ; Set DS <- real_mode_seg
1952                 pop ds
1953 get_next_opt:   lodsb
1954                 and al,al
1955                 jz near cmdline_end
1956                 cmp al,' '
1957                 jbe get_next_opt
1958                 dec si
1959                 mov eax,[si]
1960                 cmp eax,'vga='
1961                 je is_vga_cmd
1962                 cmp eax,'mem='
1963                 je is_mem_cmd
1964                 push es                         ; Save ES -> real_mode_seg
1965                 push cs
1966                 pop es                          ; Set ES <- normal DS
1967                 mov di,initrd_cmd
1968                 mov cx,initrd_cmd_len
1969                 repe cmpsb
1970                 jne not_initrd
1971                 mov di,InitRD
1972                 push si                         ; mangle_dir mangles si
1973                 call mangle_name                ; Mangle ramdisk name
1974                 pop si
1975                 cmp byte [es:InitRD],' '        ; Null filename?
1976                 seta byte [es:initrd_flag]      ; Set flag if not
1977 not_initrd:     pop es                          ; Restore ES -> real_mode_seg
1978 skip_this_opt:  lodsb                           ; Load from command line
1979                 cmp al,' '
1980                 ja skip_this_opt
1981                 dec si
1982                 jmp short get_next_opt
1983 is_vga_cmd:
1984                 add si,byte 4
1985                 mov eax,[si]
1986                 mov bx,-1
1987                 cmp eax, 'norm'                 ; vga=normal
1988                 je vc0
1989                 and eax,0ffffffh                ; 3 bytes
1990                 mov bx,-2
1991                 cmp eax, 'ext'                  ; vga=ext
1992                 je vc0
1993                 mov bx,-3
1994                 cmp eax, 'ask'                  ; vga=ask
1995                 je vc0
1996                 call parseint                   ; vga=<number>
1997                 jc skip_this_opt                ; Not an integer
1998 vc0:            mov [bs_vidmode],bx             ; Set video mode
1999                 jmp short skip_this_opt
2000 is_mem_cmd:
2001                 add si,byte 4
2002                 call parseint
2003                 jc skip_this_opt                ; Not an integer
2004                 mov [cs:HighMemSize],ebx
2005                 jmp short skip_this_opt
2006 cmdline_end:
2007                 push cs                         ; Restore standard DS
2008                 pop ds
2009                 sub si,cmd_line_here
2010                 mov [CmdLineLen],si             ; Length including final null
2011 ;
2012 ; Now check if we have a large kernel, which needs to be loaded high
2013 ;
2014                 mov dword [RamdiskMax], HIGHMEM_MAX     ; Default initrd limit
2015                 cmp dword [es:su_header],HEADER_ID      ; New setup code ID
2016                 jne near old_kernel             ; Old kernel, load low
2017                 cmp word [es:su_version],0200h  ; Setup code version 2.0
2018                 jb near old_kernel              ; Old kernel, load low
2019                 cmp word [es:su_version],0201h  ; Version 2.01+?
2020                 jb new_kernel                   ; If 2.00, skip this step
2021                 mov word [es:su_heapend],linux_stack    ; Set up the heap
2022                 or byte [es:su_loadflags],80h   ; Let the kernel know we care
2023                 cmp word [es:su_version],0203h  ; Version 2.03+?
2024                 jb new_kernel                   ; Not 2.03+
2025                 mov eax,[es:su_ramdisk_max]
2026                 mov [RamdiskMax],eax            ; Set the ramdisk limit
2027
2028 ;
2029 ; We definitely have a new-style kernel.  Let the kernel know who we are,
2030 ; and that we are clueful
2031 ;
2032 new_kernel:
2033                 mov byte [es:su_loader],syslinux_id     ; Show some ID
2034                 movzx ax,byte [es:bs_setupsecs] ; Variable # of setup sectors
2035                 mov [SetupSecs],ax
2036 ;
2037 ; About to load the kernel.  This is a modern kernel, so use the boot flags
2038 ; we were provided.
2039 ;
2040                 mov al,[es:su_loadflags]
2041                 mov [LoadFlags],al
2042 ;
2043 ; Load the kernel.  We always load it at 100000h even if we're supposed to
2044 ; load it "low"; for a "low" load we copy it down to low memory right before
2045 ; jumping to it.
2046 ;
2047 read_kernel:
2048                 mov si,KernelCName              ; Print kernel name part of
2049                 call cwritestr                  ; "Loading" message
2050                 mov si,dotdot_msg               ; Print dots
2051                 call cwritestr
2052
2053                 mov eax,[HighMemSize]
2054                 sub eax,100000h                 ; Load address
2055                 cmp eax,[KernelSize]
2056                 jb near no_high_mem             ; Not enough high memory
2057 ;
2058 ; Move the stuff beyond the setup code to high memory at 100000h
2059 ;
2060                 movzx esi,word [SetupSecs]      ; Setup sectors
2061                 inc esi                         ; plus 1 boot sector
2062                 shl esi,9                       ; Convert to bytes
2063                 mov ecx,8000h                   ; 32K
2064                 sub ecx,esi                     ; Number of bytes to copy
2065                 push ecx
2066                 shr ecx,2                       ; Convert to dwords
2067                 add esi,(real_mode_seg << 4)    ; Pointer to source
2068                 mov edi,100000h                 ; Copy to address 100000h
2069                 call bcopy                      ; Transfer to high memory
2070
2071                 ; On exit EDI -> where to load the rest
2072
2073                 mov si,dot_msg                  ; Progress report
2074                 call cwritestr
2075                 call abort_check
2076
2077                 pop ecx                         ; Number of bytes in the initial portion
2078                 pop si                          ; Restore file handle/cluster pointer
2079                 mov eax,[KernelSize]
2080                 sub eax,ecx                     ; Amount of kernel left over
2081                 jbe high_load_done              ; Zero left (tiny kernel)
2082
2083                 call load_high                  ; Copy the file
2084
2085 high_load_done:
2086                 mov ax,real_mode_seg            ; Set to real mode seg
2087                 mov es,ax
2088
2089                 mov si,dot_msg
2090                 call cwritestr
2091
2092 ;
2093 ; Now see if we have an initial RAMdisk; if so, do requisite computation
2094 ; We know we have a new kernel; the old_kernel code already will have objected
2095 ; if we tried to load initrd using an old kernel
2096 ;
2097 load_initrd:
2098                 test byte [initrd_flag],1
2099                 jz near nk_noinitrd
2100                 push es                         ; ES->real_mode_seg
2101                 push ds
2102                 pop es                          ; We need ES==DS
2103                 mov si,InitRD
2104                 mov di,InitRDCName
2105                 call unmangle_name              ; Create human-readable name
2106                 sub di,InitRDCName
2107                 mov [InitRDCNameLen],di
2108                 mov di,InitRD
2109                 call searchdir                  ; Look for it in directory
2110                 pop es
2111                 jz initrd_notthere
2112                 mov [es:su_ramdisklen1],ax      ; Ram disk length
2113                 mov [es:su_ramdisklen2],dx
2114                 mov edx,[HighMemSize]           ; End of memory
2115                 dec edx
2116                 mov eax,[RamdiskMax]            ; Highest address allowed by kernel
2117                 cmp edx,eax
2118                 jna memsize_ok
2119                 mov edx,eax                     ; Adjust to fit inside limit
2120 memsize_ok:
2121                 inc edx
2122                 xor dx,dx                       ; Round down to 64K boundary
2123                 sub edx,[es:su_ramdisklen]      ; Subtract size of ramdisk
2124                 xor dx,dx                       ; Round down to 64K boundary
2125                 mov [es:su_ramdiskat],edx       ; Load address
2126                 call loadinitrd                 ; Load initial ramdisk
2127                 jmp short initrd_end
2128
2129 initrd_notthere:
2130                 mov si,err_noinitrd
2131                 call cwritestr
2132                 mov si,InitRDCName
2133                 call cwritestr
2134                 mov si,crlf_msg
2135                 jmp abort_load
2136
2137 no_high_mem:    mov si,err_nohighmem            ; Error routine
2138                 jmp abort_load
2139
2140 initrd_end:
2141 nk_noinitrd:
2142 ;
2143 ; Abandon hope, ye that enter here!  We do no longer permit aborts.
2144 ;
2145                 call abort_check                ; Last chance!!
2146
2147                 mov si,ready_msg
2148                 call cwritestr
2149
2150                 call vgaclearmode               ; We can't trust ourselves after this
2151 ;
2152 ; Now, if we were supposed to load "low", copy the kernel down to 10000h
2153 ; and the real mode stuff to 90000h.  We assume that all bzImage kernels are
2154 ; capable of starting their setup from a different address.
2155 ;
2156                 mov ax,real_mode_seg
2157                 mov fs,ax
2158
2159 ;
2160 ; Copy command line.  Unfortunately, the kernel boot protocol requires
2161 ; the command line to exist in the 9xxxxh range even if the rest of the
2162 ; setup doesn't.
2163 ;
2164                 cli                             ; In case of hooked interrupts
2165                 test byte [LoadFlags],LOAD_HIGH
2166                 jz need_high_cmdline
2167                 cmp word [fs:su_version],0202h  ; Support new cmdline protocol?
2168                 jb need_high_cmdline
2169                 ; New cmdline protocol
2170                 ; Store 32-bit (flat) pointer to command line
2171                 mov dword [fs:su_cmd_line_ptr],(real_mode_seg << 4) + cmd_line_here
2172                 jmp short in_proper_place
2173
2174 need_high_cmdline:
2175 ;
2176 ; Copy command line up to 90000h
2177 ;
2178                 mov ax,9000h
2179                 mov es,ax
2180                 mov si,cmd_line_here
2181                 mov di,si
2182                 mov [fs:kern_cmd_magic],word CMD_MAGIC ; Store magic
2183                 mov [fs:kern_cmd_offset],di     ; Store pointer
2184
2185                 mov cx,[CmdLineLen]
2186                 add cx,byte 3
2187                 shr cx,2                        ; Convert to dwords
2188                 fs rep movsd
2189
2190                 push fs
2191                 pop es
2192
2193                 test byte [LoadFlags],LOAD_HIGH
2194                 jnz in_proper_place             ; If high load, we're done
2195
2196 ;
2197 ; Loading low; we can't assume it's safe to run in place.
2198 ;
2199 ; Copy real_mode stuff up to 90000h
2200 ;
2201                 mov ax,9000h
2202                 mov es,ax
2203                 mov cx,[SetupSecs]
2204                 inc cx                          ; Setup + boot sector
2205                 shl cx,7                        ; Sectors -> dwords
2206                 xor si,si
2207                 xor di,di
2208                 fs rep movsd                    ; Copy setup + boot sector
2209 ;
2210 ; Some kernels in the 1.2 ballpark but pre-bzImage have more than 4
2211 ; setup sectors, but the boot protocol had not yet been defined.  They
2212 ; rely on a signature to figure out if they need to copy stuff from
2213 ; the "protected mode" kernel area.  Unfortunately, we used that area
2214 ; as a transfer buffer, so it's going to find the signature there.
2215 ; Hence, zero the low 32K beyond the setup area.
2216 ;
2217                 mov di,[SetupSecs]
2218                 inc di                          ; Setup + boot sector
2219                 mov cx,32768/512                ; Sectors/32K
2220                 sub cx,di                       ; Remaining sectors
2221                 shl di,9                        ; Sectors -> bytes
2222                 shl cx,7                        ; Sectors -> dwords
2223                 xor eax,eax
2224                 rep stosd                       ; Clear region
2225 ;
2226 ; Copy the kernel down to the "low" location
2227 ;
2228                 mov ecx,[KernelSize]
2229                 add ecx,3                       ; Round upwards
2230                 shr ecx,2                       ; Bytes -> dwords
2231                 mov esi,100000h
2232                 mov edi,10000h
2233                 call bcopy
2234
2235 ;
2236 ; Now everything is where it needs to be...
2237 ;
2238 ; When we get here, es points to the final segment, either
2239 ; 9000h or real_mode_seg
2240 ;
2241 in_proper_place:
2242
2243 ;
2244 ; If the default root device is set to FLOPPY (0000h), change to
2245 ; /dev/fd0 (0200h)
2246 ;
2247                 cmp word [es:bs_rootdev],byte 0
2248                 jne root_not_floppy
2249                 mov word [es:bs_rootdev],0200h
2250 root_not_floppy:
2251 ;
2252 ; Copy the disk table to high memory, then re-initialize the floppy
2253 ; controller
2254 ;
2255 ; This needs to be moved before the copy
2256 ;
2257 %if 0
2258                 push ds
2259                 push bx
2260                 lds si,[fdctab]
2261                 mov di,linux_fdctab
2262                 mov cx,3                        ; 12 bytes
2263                 push di
2264                 rep movsd
2265                 pop di
2266                 mov [fdctab1],di                ; Save new floppy tab pos
2267                 mov [fdctab2],es
2268                 xor ax,ax
2269                 xor dx,dx
2270                 int 13h
2271                 pop bx
2272                 pop ds
2273 %endif
2274 ;
2275 ; Linux wants the floppy motor shut off before starting the kernel,
2276 ; at least bootsect.S seems to imply so
2277 ;
2278 kill_motor:
2279                 mov dx,03F2h
2280                 xor al,al
2281                 call slow_out
2282 ;
2283 ; If we're debugging, wait for a keypress so we can read any debug messages
2284 ;
2285 %ifdef debug
2286                 xor ax,ax
2287                 int 16h
2288 %endif
2289 ;
2290 ; Set up segment registers and the Linux real-mode stack
2291 ; Note: es == the real mode segment
2292 ;
2293                 cli
2294                 mov bx,es
2295                 mov ds,bx
2296                 mov fs,bx
2297                 mov gs,bx
2298                 mov ss,bx
2299                 mov sp,linux_stack
2300 ;
2301 ; We're done... now RUN THAT KERNEL!!!!
2302 ; Setup segment == real mode segment + 020h; we need to jump to offset
2303 ; zero in the real mode segment.
2304 ;
2305                 add bx,020h
2306                 push bx
2307                 push word 0h
2308                 retf
2309
2310 ;
2311 ; Load an older kernel.  Older kernels always have 4 setup sectors, can't have
2312 ; initrd, and are always loaded low.
2313 ;
2314 old_kernel:
2315                 test byte [initrd_flag],1       ; Old kernel can't have initrd
2316                 jz load_old_kernel
2317                 mov si,err_oldkernel
2318                 jmp abort_load
2319 load_old_kernel:
2320                 mov word [SetupSecs],4          ; Always 4 setup sectors
2321                 mov byte [LoadFlags],0          ; Always low
2322                 jmp read_kernel
2323
2324 ;
2325 ; Load a COMBOOT image.  A COMBOOT image is basically a DOS .COM file,
2326 ; except that it may, of course, not contain any DOS system calls.  We
2327 ; do, however, allow the execution of INT 20h to return to SYSLINUX.
2328 ;
2329 is_comboot_image:
2330                 and dx,dx
2331                 jnz comboot_too_large
2332                 cmp ax,0ff00h           ; Max size in bytes
2333                 jae comboot_too_large
2334
2335                 ;
2336                 ; Set up the DOS vectors in the IVT (INT 20h-3fh)
2337                 ;
2338                 mov dword [4*0x20],comboot_return       ; INT 20h vector
2339                 mov eax,comboot_bogus
2340                 mov di,4*0x21
2341                 mov cx,31               ; All remaining DOS vectors
2342                 rep stosd
2343         
2344                 mov cx,comboot_seg
2345                 mov es,cx
2346
2347                 mov bx,100h             ; Load at <seg>:0100h
2348
2349                 mov cx,[ClustPerMoby]   ; Absolute maximum # of clusters
2350                 call getfssec
2351
2352                 xor di,di
2353                 mov cx,64               ; 256 bytes (size of PSP)
2354                 xor eax,eax             ; Clear PSP
2355                 rep stosd
2356
2357                 mov word [es:0], 020CDh ; INT 20h instruction
2358                 ; First non-free paragraph
2359                 mov word [es:02h], comboot_seg+1000h
2360
2361                 ; Copy the command line from high memory
2362                 mov cx,125              ; Max cmdline len (minus space and CR)
2363                 mov si,[CmdOptPtr]
2364                 mov di,081h             ; Offset in PSP for command line
2365                 mov al,' '              ; DOS command lines begin with a space
2366                 stosb
2367
2368 comboot_cmd_cp: lodsb
2369                 and al,al
2370                 jz comboot_end_cmd
2371                 stosb
2372                 loop comboot_cmd_cp
2373 comboot_end_cmd: mov al,0Dh             ; CR after last character
2374                 stosb
2375                 mov al,126              ; Include space but not CR
2376                 sub al,cl
2377                 mov [es:80h], al        ; Store command line length
2378
2379                 call vgaclearmode       ; Reset video
2380
2381                 mov ax,es
2382                 mov ds,ax
2383                 mov ss,ax
2384                 xor sp,sp
2385                 push word 0             ; Return to address 0 -> exit
2386
2387                 jmp comboot_seg:100h    ; Run it
2388
2389 ; Looks like a COMBOOT image but too large
2390 comboot_too_large:
2391                 mov si,err_comlarge
2392                 call cwritestr
2393 cb_enter:       jmp enter_command
2394
2395 ; Proper return vector
2396 comboot_return: cli                     ; Don't trust anyone
2397                 xor ax,ax
2398                 mov ss,ax
2399                 mov sp,[ss:SavedSP]
2400                 mov ds,ax
2401                 mov es,ax
2402                 sti
2403                 cld
2404                 jmp short cb_enter
2405
2406 ; Attempted to execute DOS system call
2407 comboot_bogus:  cli                     ; Don't trust anyone
2408                 xor ax,ax
2409                 mov ss,ax
2410                 mov sp,[ss:SavedSP]
2411                 mov ds,ax
2412                 mov es,ax
2413                 sti
2414                 cld
2415                 mov si,KernelCName
2416                 call cwritestr
2417                 mov si,err_notdos
2418                 call cwritestr
2419                 jmp short cb_enter
2420
2421 ;
2422 ; Load a boot sector
2423 ;
2424 is_bootsector:
2425                 ; Transfer zero bytes
2426                 push word 0
2427                 jmp short load_bootsec
2428 is_bss_sector:
2429                 ; Transfer the superblock
2430                 push word superblock_len
2431 load_bootsec:
2432                 and dx,dx
2433                 jnz bad_bootsec
2434                 mov bx,[bsBytesPerSec]
2435                 cmp ax,bx
2436                 jne bad_bootsec
2437
2438                 ; Make sure we don't test this uninitialized
2439                 mov [bx+trackbuf-2],dx  ; Note DX == 0
2440
2441                 mov bx,trackbuf
2442                 mov cx,1                ; 1 cluster >= 1 sector
2443                 call getfssec
2444
2445                 mov bx,[bsBytesPerSec]
2446                 mov ax,[bx+trackbuf-2]
2447                 cmp ax,0AA55h           ; Boot sector signature
2448                 jne bad_bootsec
2449
2450                 mov si,superblock
2451                 mov di,trackbuf+(superblock-bootsec)
2452                 pop cx                  ; Transfer count
2453                 rep movsb
2454 ;
2455 ; Okay, here we go... copy over our own boot sector and run the new one
2456 ;
2457                 call vgaclearmode       ; Reset video
2458
2459                 cli                     ; Point of no return
2460         
2461                 mov dl,[bsDriveNumber]  ; May not be in new bootsector!
2462
2463                 mov si,trackbuf
2464                 mov di,bootsec
2465                 mov cx,[bsBytesPerSec]
2466                 rep movsb               ; Copy the boot sector!
2467                 
2468                 mov si,PartInfo
2469                 mov di,800h-18          ; Put partition info here
2470                 push di
2471                 mov cx,8                ; 16 bytes
2472                 rep movsw
2473                 pop si                  ; DS:SI points to partition info
2474
2475                 jmp bootsec
2476
2477 bad_bootsec:
2478                 mov si,err_bootsec
2479                 call cwritestr
2480                 jmp enter_command
2481
2482 ;
2483 ; 32-bit bcopy routine for real mode
2484 ;
2485 ; We enter protected mode, set up a flat 32-bit environment, run rep movsd
2486 ; and then exit.  IMPORTANT: This code assumes cs == ss == 0.
2487 ;
2488 ; This code is probably excessively anal-retentive in its handling of
2489 ; segments, but this stuff is painful enough as it is without having to rely
2490 ; on everything happening "as it ought to."
2491 ;
2492                 align 4
2493 bcopy_gdt:      dw bcopy_gdt_size-1     ; Null descriptor - contains GDT
2494                 dd bcopy_gdt            ; pointer for LGDT instruction
2495                 dw 0
2496                 dd 0000ffffh            ; Code segment, use16, readable,
2497                 dd 00009b00h            ; present, dpl 0, cover 64K
2498                 dd 0000ffffh            ; Data segment, use16, read/write,
2499                 dd 008f9300h            ; present, dpl 0, cover all 4G
2500                 dd 0000ffffh            ; Data segment, use16, read/write,
2501                 dd 00009300h            ; present, dpl 0, cover 64K
2502 bcopy_gdt_size: equ $-bcopy_gdt
2503
2504 bcopy:          push eax
2505                 pushf                   ; Saves, among others, the IF flag
2506                 push gs
2507                 push fs
2508                 push ds
2509                 push es
2510
2511                 cli
2512                 call enable_a20
2513
2514                 o32 lgdt [cs:bcopy_gdt]
2515                 mov eax,cr0
2516                 or al,1
2517                 mov cr0,eax             ; Enter protected mode
2518                 jmp 08h:.in_pm
2519
2520 .in_pm:         mov ax,10h              ; Data segment selector
2521                 mov es,ax
2522                 mov ds,ax
2523
2524                 mov al,18h              ; "Real-mode-like" data segment
2525                 mov ss,ax
2526                 mov fs,ax
2527                 mov gs,ax       
2528         
2529                 a32 rep movsd           ; Do our business
2530                 
2531                 mov es,ax               ; Set to "real-mode-like"
2532                 mov ds,ax
2533         
2534                 mov eax,cr0
2535                 and al,~1
2536                 mov cr0,eax             ; Disable protected mode
2537                 jmp 0:.in_rm
2538
2539 .in_rm:         xor ax,ax               ; Back in real mode
2540                 mov ss,ax
2541                 pop es
2542                 pop ds
2543                 pop fs
2544                 pop gs
2545                 call disable_a20
2546
2547                 popf                    ; Re-enables interrupts
2548                 pop eax
2549                 ret
2550
2551 ;
2552 ; Routines to enable and disable (yuck) A20.  These routines are gathered
2553 ; from tips from a couple of sources, including the Linux kernel and
2554 ; http://www.x86.org/.  The need for the delay to be as large as given here
2555 ; is indicated by Donnie Barnes of RedHat, the problematic system being an
2556 ; IBM ThinkPad 760EL.
2557 ;
2558 ; We typically toggle A20 twice for every 64K transferred.
2559
2560 %define io_delay        call _io_delay
2561 %define IO_DELAY_PORT   80h             ; Invalid port (we hope!)
2562 %define disable_wait    32              ; How long to wait for a disable
2563
2564 %define A20_DUNNO       0               ; A20 type unknown
2565 %define A20_NONE        1               ; A20 always on?
2566 %define A20_BIOS        2               ; A20 BIOS enable
2567 %define A20_KBC         3               ; A20 through KBC
2568 %define A20_FAST        4               ; A20 through port 92h
2569
2570 slow_out:       out dx, al              ; Fall through
2571
2572 _io_delay:      out IO_DELAY_PORT,al
2573                 out IO_DELAY_PORT,al
2574                 ret
2575
2576 enable_a20:
2577                 pushad
2578                 mov byte [cs:A20Tries],255 ; Times to try to make this work
2579
2580 try_enable_a20:
2581 ;
2582 ; Flush the caches
2583 ;
2584 ;               call try_wbinvd
2585
2586 ;
2587 ; If the A20 type is known, jump straight to type
2588 ;
2589                 mov bp,[cs:A20Type]
2590                 add bp,bp                       ; Convert to word offset
2591                 jmp word [cs:bp+A20List]
2592
2593 ;
2594 ; First, see if we are on a system with no A20 gate
2595 ;
2596 a20_dunno:
2597 a20_none:
2598                 mov byte [cs:A20Type], A20_NONE
2599                 call a20_test
2600                 jnz a20_done
2601
2602 ;
2603 ; Next, try the BIOS (INT 15h AX=2401h)
2604 ;
2605 a20_bios:
2606                 mov byte [cs:A20Type], A20_BIOS
2607                 mov ax,2401h
2608                 pushf                           ; Some BIOSes muck with IF
2609                 int 15h
2610                 popf
2611
2612                 call a20_test
2613                 jnz a20_done
2614
2615 ;
2616 ; Enable the keyboard controller A20 gate
2617 ;
2618 a20_kbc:
2619                 mov dl, 1                       ; Allow early exit
2620                 call empty_8042
2621                 jnz a20_done                    ; A20 live, no need to use KBC
2622
2623                 mov byte [cs:A20Type], A20_KBC  ; Starting KBC command sequence
2624
2625                 mov al,0D1h                     ; Command write
2626                 out 064h, al
2627                 call empty_8042_uncond
2628
2629                 mov al,0DFh                     ; A20 on
2630                 out 060h, al
2631                 call empty_8042_uncond
2632
2633                 ; Verify that A20 actually is enabled.  Do that by
2634                 ; observing a word in low memory and the same word in
2635                 ; the HMA until they are no longer coherent.  Note that
2636                 ; we don't do the same check in the disable case, because
2637                 ; we don't want to *require* A20 masking (SYSLINUX should
2638                 ; work fine without it, if the BIOS does.)
2639 .kbc_wait:      push cx
2640                 xor cx,cx
2641 .kbc_wait_loop:
2642                 call a20_test
2643                 jnz a20_done_pop
2644                 loop .kbc_wait_loop
2645
2646                 pop cx
2647 ;
2648 ; Running out of options here.  Final attempt: enable the "fast A20 gate"
2649 ;
2650 a20_fast:
2651                 mov byte [cs:A20Type], A20_FAST ; Haven't used the KBC yet
2652                 in al, 092h
2653                 or al,02h
2654                 and al,~01h                     ; Don't accidentally reset the machine!
2655                 out 092h, al
2656
2657 .fast_wait:     push cx
2658                 xor cx,cx
2659 .fast_wait_loop:
2660                 call a20_test
2661                 jnz a20_done_pop
2662                 loop .fast_wait_loop
2663
2664                 pop cx
2665
2666 ;
2667 ; Oh bugger.  A20 is not responding.  Try frobbing it again; eventually give up
2668 ; and report failure to the user.
2669 ;
2670
2671
2672                 dec byte [cs:A20Tries]
2673                 jnz try_enable_a20
2674
2675                 mov si, err_a20
2676                 jmp abort_load
2677 ;
2678 ; A20 unmasked, proceed...
2679 ;
2680 a20_done_pop:   pop cx
2681 a20_done:       popad
2682                 ret
2683
2684 ;
2685 ; This routine tests if A20 is enabled (ZF = 0).  This routine
2686 ; must not destroy any register contents.
2687 ;
2688 a20_test:
2689                 push es
2690                 push cx
2691                 push ax
2692                 mov cx,0FFFFh           ; HMA = segment 0FFFFh
2693                 mov es,cx
2694                 mov cx,32               ; Loop count
2695                 mov ax,[cs:A20Test]
2696 .a20_wait:      inc ax
2697                 mov [cs:A20Test],ax
2698                 io_delay                ; Serialize, and fix delay
2699                 cmp ax,[es:A20Test+10h]
2700                 loopz .a20_wait
2701 .a20_done:      pop ax
2702                 pop cx
2703                 pop es
2704                 ret
2705
2706 disable_a20:
2707                 pushad
2708 ;
2709 ; Flush the caches
2710 ;
2711 ;               call try_wbinvd
2712
2713                 mov bp,[cs:A20Type]
2714                 add bp,bp                       ; Convert to word offset
2715                 jmp word [cs:bp+A20DList]
2716
2717 a20d_bios:
2718                 mov ax,2400h
2719                 pushf                           ; Some BIOSes muck with IF
2720                 int 15h
2721                 popf
2722                 jmp short a20d_snooze
2723
2724 ;
2725 ; Disable the "fast A20 gate"
2726 ;
2727 a20d_fast:
2728                 in al, 092h
2729                 and al,~03h
2730                 out 092h, al
2731                 jmp short a20d_snooze
2732
2733 ;
2734 ; Disable the keyboard controller A20 gate
2735 ;
2736 a20d_kbc:
2737                 call empty_8042_uncond
2738                 mov al,0D1h
2739                 out 064h, al            ; Command write
2740                 call empty_8042_uncond
2741                 mov al,0DDh             ; A20 off
2742                 out 060h, al
2743                 call empty_8042_uncond
2744                 ; Wait a bit for it to take effect
2745 a20d_snooze:
2746                 push cx
2747                 mov cx, disable_wait
2748 .delayloop:     call a20_test
2749                 jz .disabled
2750                 loop .delayloop
2751 .disabled:      pop cx
2752 a20d_dunno:
2753 a20d_none:
2754                 popad
2755                 ret
2756
2757 ;
2758 ; Routine to empty the 8042 KBC controller.  If dl != 0
2759 ; then we will test A20 in the loop and exit if A20 is
2760 ; suddenly enabled.
2761 ;
2762 empty_8042_uncond:
2763                 xor dl,dl
2764 empty_8042:
2765                 call a20_test
2766                 jz .a20_on
2767                 and dl,dl
2768                 jnz .done
2769 .a20_on:        io_delay
2770                 in al, 064h             ; Status port
2771                 test al,1
2772                 jz .no_output
2773                 io_delay
2774                 in al, 060h             ; Read input
2775                 jmp short empty_8042
2776 .no_output:
2777                 test al,2
2778                 jnz empty_8042
2779                 io_delay
2780 .done:          ret     
2781
2782 ;
2783 ; WBINVD instruction; gets auto-eliminated on 386 CPUs
2784 ;
2785 try_wbinvd:
2786                 wbinvd
2787                 ret
2788
2789 ;
2790 ; Load RAM disk into high memory
2791 ;
2792 ; Need to be set:
2793 ;       su_ramdiskat    - Where in memory to load
2794 ;       su_ramdisklen   - Size of file
2795 ;       SI              - initrd filehandle/cluster pointer
2796 ;
2797 loadinitrd:
2798                 push es                         ; Save ES on entry
2799                 mov ax,real_mode_seg
2800                 mov es,ax
2801                 mov edi,[es:su_ramdiskat]       ; initrd load address
2802                 push si
2803                 mov si,crlfloading_msg          ; Write "Loading "
2804                 call cwritestr
2805                 mov si,InitRDCName              ; Write ramdisk name
2806                 call cwritestr
2807                 mov si,dotdot_msg               ; Write dots
2808                 call cwritestr
2809                 pop si
2810
2811                 mov eax,[es:su_ramdisklen]
2812                 call load_high                  ; Load the file
2813
2814                 call crlf
2815                 pop es                          ; Restore original ES
2816                 ret
2817
2818 ;
2819 ; load_high:    loads (the remainder of) a file into high memory.
2820 ;               This routine prints dots for each 64K transferred, and
2821 ;               calls abort_check periodically.
2822
2823 ;               The xfer_buf_seg is used as a bounce buffer.
2824 ;
2825 ;               The input address (EDI) should be dword aligned, and the final
2826 ;               dword written is padded with zeroes if necessary.
2827 ;
2828 ; Inputs:       SI  = file handle/cluster pointer
2829 ;               EDI = target address in high memory
2830 ;               EAX = size of remaining file in bytes
2831 ;
2832 ; Outputs:      SI  = file handle/cluster pointer
2833 ;               EDI = first untouched address (not including padding)
2834 ;
2835 load_high:
2836                 push es
2837
2838                 mov bx,xfer_buf_seg
2839                 mov es,bx
2840
2841 .read_loop:
2842                 and si,si                       ; If SI == 0 then we have end of file
2843                 jz .eof
2844                 push si
2845                 mov si,dot_msg
2846                 call cwritestr
2847                 pop si
2848                 call abort_check
2849
2850                 push eax                        ; <A> Total bytes to transfer
2851                 cmp eax,(1 << 16)               ; Max 64K in one transfer
2852                 jna .size_ok
2853                 mov eax,(1 << 16)
2854 .size_ok:
2855                 xor edx,edx
2856                 push eax                        ; <B> Bytes transferred this chunk
2857                 movzx ecx,word [ClustSize]
2858                 div ecx                         ; Convert to clusters
2859                 ; Round up...
2860                 add edx,byte -1                 ; Sets CF if EDX >= 1
2861                 adc eax,byte 0                  ; Add 1 to EAX if CF set
2862
2863                 ; Now (e)ax contains the number of clusters to get
2864                 push edi                        ; <C> Target buffer
2865                 mov cx,ax
2866                 xor bx,bx                       ; ES:0
2867                 call getfssec                   ; Load the data into xfer_buf_seg
2868                 pop edi                         ; <C> Target buffer
2869                 pop ecx                         ; <B> Byte count this round
2870                 push ecx                        ; <B> Byte count this round 
2871                 push edi                        ; <C> Target buffer
2872 .fix_slop:
2873                 test cl,3
2874                 jz .noslop
2875                 ; The last dword fractional - pad with zeroes
2876                 ; Zero-padding is critical for multi-file initramfs.
2877                 mov byte [es:ecx],0
2878                 inc ecx
2879                 jmp short .fix_slop
2880 .noslop:
2881                 shr ecx,2                       ; Convert to dwords
2882                 push esi                        ; <D> File handle/cluster pointer
2883                 mov esi,(xfer_buf_seg << 4)     ; Source address
2884                 call bcopy                      ; Copy to high memory
2885                 pop esi                         ; <D> File handle/cluster pointer
2886                 pop edi                         ; <C> Target buffer
2887                 pop ecx                         ; <B> Byte count this round
2888                 pop eax                         ; <A> Total bytes to transfer
2889                 add edi,ecx
2890                 sub eax,ecx
2891                 jnz .read_loop                  ; More to read...
2892                 
2893 .eof:
2894                 pop es
2895                 ret
2896
2897 ;
2898 ; abort_check: let the user abort with <ESC> or <Ctrl-C>
2899 ;
2900 abort_check:
2901                 call pollchar
2902                 jz ac_ret1
2903                 pusha
2904                 call getchar
2905                 cmp al,27                       ; <ESC>
2906                 je ac_kill
2907                 cmp al,3                        ; <Ctrl-C>
2908                 jne ac_ret2
2909 ac_kill:        mov si,aborted_msg
2910
2911 ;
2912 ; abort_load: Called by various routines which wants to print a fatal
2913 ;             error message and return to the command prompt.  Since this
2914 ;             may happen at just about any stage of the boot process, assume
2915 ;             our state is messed up, and just reset the segment registers
2916 ;             and the stack forcibly.
2917 ;
2918 ;             SI    = offset (in _text) of error message to print
2919 ;
2920 abort_load:
2921                 mov ax,cs                       ; Restore CS = DS = ES
2922                 mov ds,ax
2923                 mov es,ax
2924                 cli
2925                 mov sp,StackBuf-2*3             ; Reset stack
2926                 mov ss,ax                       ; Just in case...
2927                 sti
2928                 call cwritestr                  ; Expects SI -> error msg
2929 al_ok:          jmp enter_command               ; Return to command prompt
2930 ;
2931 ; End of abort_check
2932 ;
2933 ac_ret2:        popa
2934 ac_ret1:        ret
2935
2936 ;
2937 ; searchdir: Search the root directory for a pre-mangled filename in
2938 ;            DS:DI.  This routine is similar to the one in the boot
2939 ;            sector, but is a little less Draconian when it comes to
2940 ;            error handling, plus it reads the root directory in
2941 ;            larger chunks than a sector at a time (which is probably
2942 ;            a waste of coding effort, but I like to do things right).
2943 ;
2944 ;            FIXME: usually we can load the entire root dir in memory,
2945 ;            and files are usually at the beginning anyway.  It probably
2946 ;            would be worthwhile to remember if we have the first chunk
2947 ;            in memory and skip the load if that (it would speed up online
2948 ;            help, mainly.)
2949 ;
2950 ;            NOTE: This file considers finding a zero-length file an
2951 ;            error.  This is so we don't have to deal with that special
2952 ;            case elsewhere in the program (most loops have the test
2953 ;            at the end).
2954 ;
2955 ;            If successful:
2956 ;               ZF clear
2957 ;               SI      = cluster # for the first cluster
2958 ;               DX:AX   = file length in bytes
2959 ;            If unsuccessful
2960 ;               ZF set
2961 ;
2962
2963 searchdir:
2964                 mov ax,[bsRootDirEnts]
2965                 mov [DirScanCtr],ax
2966                 mov ax,[RootDirSize]
2967                 mov [DirBlocksLeft],ax
2968                 mov ax,[RootDir1]
2969                 mov dx,[RootDir2]
2970 scan_group:
2971                 mov bp,[DirBlocksLeft]
2972                 and bp,bp
2973                 jz dir_return
2974                 cmp bp,[BufSafeSec]
2975                 jna load_last
2976                 mov bp,[BufSafeSec]
2977 load_last:
2978                 sub [DirBlocksLeft],bp
2979                 push ax
2980                 push dx
2981                 mov ax,[bsBytesPerSec]
2982                 mul bp
2983                 add ax,trackbuf-31
2984                 mov [EndofDirSec],ax    ; End of loaded
2985                 pop dx
2986                 pop ax
2987                 push bp                 ; Save number of sectors
2988                 push ax                 ; Save present location
2989                 push dx
2990                 push di                 ; Save name
2991                 mov bx,trackbuf
2992                 call getlinsec
2993                 pop di
2994                 pop dx
2995                 pop ax
2996                 pop bp
2997                 mov si,trackbuf
2998 dir_test_name:  cmp byte [si],0         ; Directory high water mark
2999                 je dir_return           ; Failed
3000                 test byte [si+11],18h   ; Check it really is a file
3001                 jnz dir_not_this
3002                 push di
3003                 push si
3004                 mov cx,11               ; Filename = 11 bytes
3005                 repe cmpsb
3006                 pop si
3007                 pop di
3008                 je dir_success
3009 dir_not_this:   add si,byte 32
3010                 dec word [DirScanCtr]
3011                 jz dir_return           ; Out of it...
3012                 cmp si,[EndofDirSec]
3013                 jb dir_test_name
3014                 add ax,bp               ; Increment linear sector number
3015                 adc dx,byte 0
3016                 jmp short scan_group
3017 dir_success:
3018                 mov ax,[si+28]          ; Length of file
3019                 mov dx,[si+30]
3020                 mov si,[si+26]          ; Cluster pointer
3021                 mov bx,ax
3022                 or bx,dx                ; Sets ZF iff DX:AX is zero
3023 dir_return:
3024 lf_ret:         ret
3025
3026 ;
3027 ; loadfont:     Load a .psf font file and install it onto the VGA console
3028 ;               (if we're not on a VGA screen then ignore.)  It is called with
3029 ;               SI and DX:AX set by routine searchdir
3030 ;
3031 loadfont:
3032                 mov bx,trackbuf                 ; The trackbuf is >= 16K; the part
3033                 mov cx,[BufSafe]                ; of a PSF file we care about is no
3034                 call getfssec                   ; more than 8K+4 bytes
3035
3036                 mov ax,[trackbuf]               ; Magic number
3037                 cmp ax,0436h
3038                 jne lf_ret
3039
3040                 mov al,[trackbuf+2]             ; File mode
3041                 cmp al,5                        ; Font modes 0-5 supported
3042                 ja lf_ret
3043
3044                 mov bh,byte [trackbuf+3]        ; Height of font
3045                 cmp bh,2                        ; VGA minimum
3046                 jb lf_ret
3047                 cmp bh,32                       ; VGA maximum
3048                 ja lf_ret
3049
3050                 ; Copy to font buffer
3051                 mov si,trackbuf+4               ; Start of font data
3052                 mov [VGAFontSize],bh
3053                 mov di,vgafontbuf
3054                 mov cx,(32*256) >> 2            ; Maximum size
3055                 rep movsd
3056
3057                 mov [UserFont], byte 1          ; Set font flag
3058
3059                 ; Fall through to use_font
3060
3061 ;
3062 ; use_font:
3063 ;       This routine activates whatever font happens to be in the
3064 ;       vgafontbuf, and updates the adjust_screen data.
3065 ;       Must be called with CS = DS = ES
3066 ;
3067 use_font:
3068                 test [UserFont], byte 1         ; Are we using a user-specified font?
3069                 jz adjust_screen                ; If not, just do the normal stuff
3070
3071                 mov bp,vgafontbuf
3072                 mov bh,[VGAFontSize]
3073
3074                 xor bl,bl                       ; Needed by both INT 10h calls
3075                 cmp [UsingVGA], byte 1          ; Are we in graphics mode?
3076                 jne .text
3077
3078 .graphics:
3079                 xor cx,cx
3080                 mov cl,bh                       ; CX = bytes/character
3081                 mov ax,480
3082                 div cl                          ; Compute char rows per screen
3083                 mov dl,al
3084                 dec al
3085                 mov [VidRows],al
3086                 mov ax,1121h                    ; Set user character table
3087                 int 10h
3088                 mov [VidCols], byte 79          ; Always 80 bytes/line
3089                 mov [TextPage], byte 0          ; Always page 0
3090                 ret     ; No need to call adjust_screen
3091
3092 .text:
3093                 mov cx,256
3094                 xor dx,dx
3095                 mov ax,1110h
3096                 int 10h                         ; Load into VGA RAM
3097
3098                 xor bl,bl
3099                 mov ax,1103h                    ; Select page 0
3100                 int 10h
3101
3102                 ; Fall through to adjust_screen
3103
3104 ;
3105 ; adjust_screen: Set the internal variables associated with the screen size.
3106 ;               This is a subroutine in case we're loading a custom font.
3107 ;
3108 adjust_screen:
3109                 mov al,[BIOS_vidrows]
3110                 and al,al
3111                 jnz vidrows_ok
3112                 mov al,24                       ; No vidrows in BIOS, assume 25
3113                                                 ; (Remember: vidrows == rows-1)
3114 vidrows_ok:     mov [VidRows],al
3115                 mov ah,0fh
3116                 int 10h                         ; Read video state
3117                 mov [TextPage],bh
3118                 dec ah                          ; Store count-1 (same as rows)
3119                 mov [VidCols],ah
3120                 ret
3121
3122 ;
3123 ; loadkeys:     Load a LILO-style keymap; SI and DX:AX set by searchdir
3124 ;
3125 loadkeys:
3126                 and dx,dx                       ; Should be 256 bytes exactly
3127                 jne loadkeys_ret
3128                 cmp ax,256
3129                 jne loadkeys_ret
3130
3131                 mov bx,trackbuf
3132                 mov cx,1                        ; 1 cluster should be >= 256 bytes
3133                 call getfssec
3134
3135                 mov si,trackbuf
3136                 mov di,KbdMap
3137                 mov cx,256 >> 2
3138                 rep movsd
3139
3140 loadkeys_ret:   ret
3141                 
3142 ;
3143 ; get_msg_file: Load a text file and write its contents to the screen,
3144 ;               interpreting color codes.  Is called with SI and DX:AX
3145 ;               set by routine searchdir
3146 ;
3147 get_msg_file:
3148                 push es
3149                 shl edx,16                      ; EDX <- DX:AX (length of file)
3150                 mov dx,ax
3151                 mov ax,xfer_buf_seg             ; Use for temporary storage
3152                 mov es,ax
3153
3154                 mov byte [TextAttribute],07h    ; Default grey on white
3155                 mov byte [DisplayMask],07h      ; Display text in all modes
3156                 call msg_initvars
3157
3158 get_msg_chunk:  push edx                        ; EDX = length of file
3159                 xor bx,bx                       ; == xbs_textbuf
3160                 mov cx,[BufSafe]
3161                 call getfssec
3162                 pop edx
3163                 push si                         ; Save current cluster
3164                 xor si,si                       ; == xbs_textbuf
3165                 mov cx,[BufSafeBytes]           ; Number of bytes left in chunk
3166 print_msg_file:
3167                 push cx
3168                 push edx
3169                 es lodsb
3170                 cmp al,1Ah                      ; DOS EOF?
3171                 je msg_done_pop
3172                 push si
3173                 mov cl,[UsingVGA]
3174                 inc cl                          ; 01h = text mode, 02h = graphics
3175                 call [NextCharJump]             ; Do what shall be done
3176                 pop si
3177                 pop edx
3178                 pop cx
3179                 dec edx
3180                 jz msg_done
3181                 loop print_msg_file
3182                 pop si
3183                 jmp short get_msg_chunk
3184 msg_done_pop:
3185                 add sp,byte 6                   ; Drop pushed EDX, CX
3186 msg_done:
3187                 pop si
3188                 pop es
3189                 ret
3190 msg_putchar:                                    ; Normal character
3191                 cmp al,0Fh                      ; ^O = color code follows
3192                 je msg_ctrl_o
3193                 cmp al,0Dh                      ; Ignore <CR>
3194                 je msg_ignore
3195                 cmp al,0Ah                      ; <LF> = newline
3196                 je msg_newline
3197                 cmp al,0Ch                      ; <FF> = clear screen
3198                 je msg_formfeed
3199                 cmp al,19h                      ; <EM> = return to text mode
3200                 je near msg_novga
3201                 cmp al,18h                      ; <CAN> = VGA filename follows
3202                 je near msg_vga
3203                 jnb .not_modectl
3204                 cmp al,10h                      ; 10h to 17h are mode controls
3205                 jae near msg_modectl
3206 .not_modectl:
3207
3208 msg_normal:     call write_serial_displaymask   ; Write to serial port
3209                 test [DisplayMask],cl
3210                 jz msg_ignore                   ; Not screen
3211                 mov bx,[TextAttrBX]
3212                 mov ah,09h                      ; Write character/attribute
3213                 mov cx,1                        ; One character only
3214                 int 10h                         ; Write to screen
3215                 mov al,[CursorCol]
3216                 inc ax
3217                 cmp al,[VidCols]
3218                 ja msg_line_wrap                ; Screen wraparound
3219                 mov [CursorCol],al
3220
3221 msg_gotoxy:     mov bh,[TextPage]
3222                 mov dx,[CursorDX]
3223                 mov ah,02h                      ; Set cursor position
3224                 int 10h
3225 msg_ignore:     ret
3226 msg_ctrl_o:                                     ; ^O = color code follows
3227                 mov word [NextCharJump],msg_setbg
3228                 ret
3229 msg_newline:                                    ; Newline char or end of line
3230                 mov si,crlf_msg
3231                 call write_serial_str_displaymask
3232 msg_line_wrap:                                  ; Screen wraparound
3233                 test [DisplayMask],cl
3234                 jz msg_ignore
3235                 mov byte [CursorCol],0
3236                 mov al,[CursorRow]
3237                 inc ax
3238                 cmp al,[VidRows]
3239                 ja msg_scroll
3240                 mov [CursorRow],al
3241                 jmp short msg_gotoxy
3242 msg_scroll:     xor cx,cx                       ; Upper left hand corner
3243                 mov dx,[ScreenSize]
3244                 mov [CursorRow],dh              ; New cursor at the bottom
3245                 mov bh,[ScrollAttribute]
3246                 mov ax,0601h                    ; Scroll up one line
3247                 int 10h
3248                 jmp short msg_gotoxy
3249 msg_formfeed:                                   ; Form feed character
3250                 mov si,crff_msg
3251                 call write_serial_str_displaymask
3252                 test [DisplayMask],cl
3253                 jz msg_ignore
3254                 xor cx,cx
3255                 mov [CursorDX],cx               ; Upper lefthand corner
3256                 mov dx,[ScreenSize]
3257                 mov bh,[TextAttribute]
3258                 mov ax,0600h                    ; Clear screen region
3259                 int 10h
3260                 jmp short msg_gotoxy
3261 msg_setbg:                                      ; Color background character
3262                 call unhexchar
3263                 jc msg_color_bad
3264                 shl al,4
3265                 test [DisplayMask],cl
3266                 jz .dontset
3267                 mov [TextAttribute],al
3268 .dontset:
3269                 mov word [NextCharJump],msg_setfg
3270                 ret
3271 msg_setfg:                                      ; Color foreground character
3272                 call unhexchar
3273                 jc msg_color_bad
3274                 test [DisplayMask],cl
3275                 jz .dontset
3276                 or [TextAttribute],al           ; setbg set foreground to 0
3277 .dontset:
3278                 jmp short msg_putcharnext
3279 msg_vga:
3280                 mov word [NextCharJump],msg_filename
3281                 mov di, VGAFileBuf
3282                 jmp short msg_setvgafileptr
3283
3284 msg_color_bad:
3285                 mov byte [TextAttribute],07h    ; Default attribute
3286 msg_putcharnext:
3287                 mov word [NextCharJump],msg_putchar
3288                 ret
3289
3290 msg_filename:                                   ; Getting VGA filename
3291                 cmp al,0Ah                      ; <LF> = end of filename
3292                 je msg_viewimage
3293                 cmp al,' '
3294                 jbe msg_ret                     ; Ignore space/control char
3295                 mov di,[VGAFilePtr]
3296                 cmp di,VGAFileBufEnd
3297                 jnb msg_ret
3298                 mov [di],al                     ; Can't use stosb (DS:)
3299                 inc di
3300 msg_setvgafileptr:
3301                 mov [VGAFilePtr],di
3302 msg_ret:        ret
3303
3304 msg_novga:
3305                 call vgaclearmode
3306                 jmp short msg_initvars
3307
3308 msg_viewimage:
3309                 push es
3310                 push ds
3311                 pop es                          ; ES <- DS
3312                 mov si,VGAFileBuf
3313                 mov di,VGAFileMBuf
3314                 push di
3315                 call mangle_name
3316                 pop di
3317                 call searchdir
3318                 pop es
3319                 jz msg_putcharnext              ; Not there
3320                 call vgadisplayfile
3321                 ; Fall through
3322
3323                 ; Subroutine to initialize variables, also needed
3324                 ; after loading a graphics file
3325 msg_initvars:
3326                 pusha
3327                 mov bh,[TextPage]
3328                 mov ah,03h                      ; Read cursor position
3329                 int 10h
3330                 mov [CursorDX],dx
3331                 popa
3332                 jmp short msg_putcharnext       ; Initialize state machine
3333
3334 msg_modectl:
3335                 and al,07h
3336                 mov [DisplayMask],al
3337                 jmp short msg_putcharnext
3338
3339 ;
3340 ; write_serial: If serial output is enabled, write character on serial port
3341 ; write_serial_displaymask: d:o, but ignore if DisplayMask & 04h == 0
3342 ;
3343 write_serial_displaymask:
3344                 test byte [DisplayMask], 04h
3345                 jz write_serial.end
3346 write_serial:
3347                 pushfd
3348                 pushad
3349                 mov bx,[SerialPort]
3350                 and bx,bx
3351                 je .noserial
3352                 push ax
3353                 mov ah,[FlowInput]
3354 .waitspace:
3355                 ; Wait for space in transmit register
3356                 lea dx,[bx+5]                   ; DX -> LSR
3357                 in al,dx
3358                 test al,20h
3359                 jz .waitspace
3360
3361                 ; Wait for input flow control
3362                 inc dx                          ; DX -> MSR
3363                 in al,dx
3364                 and al,ah
3365                 cmp al,ah
3366                 jne .waitspace  
3367 .no_flow:               
3368
3369                 xchg dx,bx                      ; DX -> THR
3370                 pop ax
3371                 call slow_out                   ; Send data
3372 .noserial:      popad
3373                 popfd
3374 .end:           ret
3375
3376 ;
3377 ; write_serial_str: write_serial for strings
3378 ; write_serial_str_displaymask: d:o, but ignore if DisplayMask & 04h == 0
3379 ;
3380 write_serial_str_displaymask:
3381                 test byte [DisplayMask], 04h
3382                 jz write_serial_str.end
3383
3384 write_serial_str:
3385 .loop           lodsb
3386                 and al,al
3387                 jz .end
3388                 call write_serial
3389                 jmp short .loop
3390 .end:           ret
3391
3392 ;
3393 ; writechr:     Write a single character in AL to the console without
3394 ;               mangling any registers
3395 ;
3396 writechr:
3397                 call write_serial       ; write to serial port if needed
3398                 pushfd
3399                 pushad
3400                 mov ah,0Eh
3401                 mov bx,0007h            ; white text on this page
3402                 int 10h
3403                 popad
3404                 popfd
3405                 ret
3406
3407 ;
3408 ; crlf: Print a newline
3409 ;
3410 crlf:           mov si,crlf_msg
3411                 ; Fall through
3412
3413 ;
3414 ; cwritestr: write a null-terminated string to the console, saving
3415 ;            registers on entry.
3416 ;
3417 cwritestr:
3418                 pushfd
3419                 pushad
3420 .top:           lodsb
3421                 and al,al
3422                 jz .end
3423                 call writechr
3424                 jmp short .top
3425 .end:           popad
3426                 popfd
3427                 ret
3428
3429 %ifdef debug
3430
3431 ;
3432 ; writehex[248]: Write a hex number in (AL, AX, EAX) to the console
3433 ;
3434 writehex2:
3435                 pushfd
3436                 pushad
3437                 rol eax,24
3438                 mov cx,2
3439                 jmp short writehex_common
3440 writehex4:
3441                 pushfd
3442                 pushad
3443                 rol eax,16
3444                 mov cx,4
3445                 jmp short writehex_common
3446 writehex8:
3447                 pushfd
3448                 pushad
3449                 mov cx,8
3450 writehex_common:
3451 .loop:          rol eax,4
3452                 push eax
3453                 and al,0Fh
3454                 cmp al,10
3455                 jae .high
3456 .low:           add al,'0'
3457                 jmp short .ischar
3458 .high:          add al,'A'-10
3459 .ischar:        call writechr
3460                 pop eax
3461                 loop .loop
3462                 popad
3463                 popfd
3464                 ret
3465
3466 ;
3467 ; crlf: write CR LF
3468 ;
3469 crlf:           push ax
3470                 mov al, 13
3471                 call writechr
3472                 mov al, 10
3473                 call writechr
3474                 pop ax
3475                 ret
3476
3477 %endif
3478
3479 ;
3480 ; pollchar: check if we have an input character pending (ZF = 0)
3481 ;
3482 pollchar:
3483                 pushad
3484                 mov ah,1                ; Poll keyboard
3485                 int 16h
3486                 jnz .done               ; Keyboard response
3487                 mov dx,[SerialPort]
3488                 and dx,dx
3489                 jz .done                ; No serial port -> no input
3490                 add dx,byte 5           ; DX -> LSR
3491                 in al,dx
3492                 test al,1               ; ZF = 0 if data pending
3493                 jz .done
3494                 inc dx                  ; DX -> MSR
3495                 mov ah,[FlowIgnore]     ; Required status bits
3496                 in al,dx
3497                 and al,ah
3498                 cmp al,ah
3499                 setne al
3500                 dec al                  ; Set ZF = 0 if equal
3501 .done:          popad
3502                 ret
3503
3504 ;
3505 ; getchar: Read a character from keyboard or serial port
3506 ;
3507 getchar:
3508 .again:         mov ah,1                ; Poll keyboard
3509                 int 16h
3510                 jnz .kbd                ; Keyboard input?
3511                 mov bx,[SerialPort]
3512                 and bx,bx
3513                 jz .again
3514                 lea dx,[bx+5]           ; DX -> LSR
3515                 in al,dx
3516                 test al,1
3517                 jz .again
3518                 inc dx                  ; DX -> MSR
3519                 mov ah,[FlowIgnore]
3520                 in al,dx
3521                 and al,ah
3522                 cmp al,ah
3523                 jne .again
3524 .serial:        xor ah,ah               ; Avoid confusion
3525                 xchg dx,bx              ; Data port
3526                 in al,dx
3527                 ret
3528 .kbd:           xor ax,ax               ; Get keyboard input
3529                 int 16h
3530                 and al,al
3531                 jz .func_key
3532                 mov bx,KbdMap           ; Convert character sets
3533                 xlatb
3534 .func_key:      ret
3535
3536 ;
3537 ;
3538 ; kaboom2: once everything is loaded, replace the part of kaboom
3539 ;          starting with "kaboom.patch" with this part
3540
3541 kaboom2:
3542                 mov si,err_bootfailed
3543                 call cwritestr
3544                 call getchar
3545                 call vgaclearmode
3546                 int 19h                 ; And try once more to boot...
3547 .norge:         jmp short .norge        ; If int 19h returned; this is the end
3548
3549 ;
3550 ; open,getc:    Load a file a character at a time for parsing in a manner
3551 ;               similar to the C library getc routine.  Only one simultaneous
3552 ;               use is supported.  Note: "open" trashes the trackbuf.
3553 ;
3554 ;               open:   Input:  mangled filename in DS:DI
3555 ;                       Output: ZF set on file not found or zero length
3556 ;
3557 ;               getc:   Output: CF set on end of file
3558 ;                               Character loaded in AL
3559 ;
3560 open:
3561                 call searchdir
3562                 jz open_return
3563                 pushf
3564                 mov [FBytes1],ax
3565                 mov [FBytes2],dx
3566                 add ax,[ClustSize]
3567                 adc dx,byte 0
3568                 sub ax,byte 1
3569                 sbb dx,byte 0
3570                 div word [ClustSize]
3571                 mov [FClust],ax         ; Number of clusters
3572                 mov [FNextClust],si     ; Cluster pointer
3573                 mov ax,[EndOfGetCBuf]   ; Pointer at end of buffer ->
3574                 mov [FPtr],ax           ;  nothing loaded yet
3575                 popf                    ; Restore no ZF
3576 open_return:    ret
3577
3578 getc:
3579                 stc                     ; If we exit here -> EOF
3580                 mov ecx,[FBytes]
3581                 jecxz getc_ret
3582                 mov si,[FPtr]
3583                 cmp si,[EndOfGetCBuf]
3584                 jb getc_loaded
3585                 ; Buffer empty -- load another set
3586                 mov cx,[FClust]
3587                 cmp cx,[BufSafe]
3588                 jna getc_oksize
3589                 mov cx,[BufSafe]
3590 getc_oksize:    sub [FClust],cx         ; Reduce remaining clusters
3591                 mov si,[FNextClust]
3592                 push es                 ; ES may be != DS, save old ES
3593                 push ds
3594                 pop es
3595                 mov bx,getcbuf
3596                 push bx
3597                 call getfssec           ; Load a trackbuf full of data
3598                 mov [FNextClust],si     ; Store new next pointer
3599                 pop si                  ; SI -> newly loaded data
3600                 pop es                  ; Restore ES
3601 getc_loaded:    lodsb                   ; Load a byte
3602                 mov [FPtr],si           ; Update next byte pointer
3603                 dec dword [FBytes]      ; Update bytes left counter
3604                 clc                     ; Not EOF
3605 getc_ret:       ret
3606
3607 ;
3608 ; ungetc:       Push a character (in AL) back into the getc buffer
3609 ;               Note: if more than one byte is pushed back, this may cause
3610 ;               bytes to be written below the getc buffer boundary.  If there
3611 ;               is a risk for this to occur, the getcbuf base address should
3612 ;               be moved up.
3613 ;
3614 ungetc:
3615                 mov si,[FPtr]
3616                 dec si
3617                 mov [si],al
3618                 mov [FPtr],si
3619                 inc dword [FBytes]
3620                 ret
3621
3622 ;
3623 ; skipspace:    Skip leading whitespace using "getc".  If we hit end-of-line
3624 ;               or end-of-file, return with carry set; ZF = true of EOF
3625 ;               ZF = false for EOLN; otherwise CF = ZF = 0.
3626 ;
3627 ;               Otherwise AL = first character after whitespace
3628 ;
3629 skipspace:
3630 skipspace_loop: call getc
3631                 jc skipspace_eof
3632                 cmp al,1Ah                      ; DOS EOF
3633                 je skipspace_eof
3634                 cmp al,0Ah
3635                 je skipspace_eoln
3636                 cmp al,' '
3637                 jbe skipspace_loop
3638                 ret                             ; CF = ZF = 0
3639 skipspace_eof:  cmp al,al                       ; Set ZF
3640                 stc                             ; Set CF
3641                 ret
3642 skipspace_eoln: add al,0FFh                     ; Set CF, clear ZF
3643                 ret
3644
3645 ;
3646 ; getkeyword:   Get a keyword from the current "getc" file; only the two
3647 ;               first characters are considered significant.
3648 ;
3649 ;               Lines beginning with ASCII characters 33-47 are treated
3650 ;               as comments and ignored; other lines are checked for
3651 ;               validity by scanning through the keywd_table.
3652 ;
3653 ;               The keyword and subsequent whitespace is skipped.
3654 ;
3655 ;               On EOF, CF = 1; otherwise, CF = 0, AL:AH = lowercase char pair
3656 ;
3657 getkeyword:
3658 gkw_find:       call skipspace
3659                 jz gkw_eof              ; end of file
3660                 jc gkw_find             ; end of line: try again
3661                 cmp al,'0'
3662                 jb gkw_skipline         ; skip comment line
3663                 push ax
3664                 call getc
3665                 pop bx
3666                 jc gkw_eof
3667                 mov bh,al               ; Move character pair into BL:BH
3668                 or bx,2020h             ; Lower-case it
3669                 mov si,keywd_table
3670 gkw_check:      lodsw
3671                 and ax,ax
3672                 jz gkw_badline          ; Bad keyword, write message
3673                 cmp ax,bx
3674                 jne gkw_check
3675                 push ax
3676 gkw_skiprest:
3677                 call getc
3678                 jc gkw_eof_pop
3679                 cmp al,'0'
3680                 ja gkw_skiprest
3681                 call ungetc
3682                 call skipspace
3683                 jz gkw_eof_pop
3684                 jc gkw_missingpar       ; Missing parameter after keyword
3685                 call ungetc             ; Return character to buffer
3686                 clc                     ; Successful return
3687 gkw_eof_pop:    pop ax
3688 gkw_eof:        ret                     ; CF = 1 on all EOF conditions
3689 gkw_missingpar: pop ax
3690                 mov si,err_noparm
3691                 call cwritestr
3692                 jmp gkw_find
3693 gkw_badline_pop: pop ax
3694 gkw_badline:    mov si,err_badcfg
3695                 call cwritestr
3696                 jmp short gkw_find
3697 gkw_skipline:   cmp al,10               ; Scan for LF
3698                 je gkw_find
3699                 call getc
3700                 jc gkw_eof
3701                 jmp short gkw_skipline
3702
3703 ;
3704 ; getint:       Load an integer from the getc file.
3705 ;               Return CF if error; otherwise return integer in EBX
3706 ;
3707 getint:
3708                 mov di,NumBuf
3709 gi_getnum:      cmp di,NumBufEnd        ; Last byte in NumBuf
3710                 jae gi_loaded
3711                 push di
3712                 call getc
3713                 pop di
3714                 jc gi_loaded
3715                 stosb
3716                 cmp al,'-'
3717                 jnb gi_getnum
3718                 call ungetc             ; Unget non-numeric
3719 gi_loaded:      mov byte [di],0
3720                 mov si,NumBuf
3721                 ; Fall through to parseint
3722
3723 ;
3724 ; parseint:     Convert an integer to a number in EBX
3725 ;               Get characters from string in DS:SI
3726 ;               Return CF on error
3727 ;               DS:SI points to first character after number
3728 ;
3729 ;               Syntaxes accepted: [-]dec, [-]0+oct, [-]0x+hex, val+K, val+M
3730 ;
3731 parseint:
3732                 push eax
3733                 push ecx
3734                 push bp
3735                 xor eax,eax             ; Current digit (keep eax == al)
3736                 mov ebx,eax             ; Accumulator
3737                 mov ecx,ebx             ; Base
3738                 xor bp,bp               ; Used for negative flag
3739 pi_begin:       lodsb
3740                 cmp al,'-'
3741                 jne pi_not_minus
3742                 xor bp,1                ; Set unary minus flag
3743                 jmp short pi_begin
3744 pi_not_minus:
3745                 cmp al,'0'
3746                 jb pi_err
3747                 je pi_octhex
3748                 cmp al,'9'
3749                 ja pi_err
3750                 mov cl,10               ; Base = decimal
3751                 jmp short pi_foundbase
3752 pi_octhex:
3753                 lodsb
3754                 cmp al,'0'
3755                 jb pi_km                ; Value is zero
3756                 or al,20h               ; Downcase
3757                 cmp al,'x'
3758                 je pi_ishex
3759                 cmp al,'7'
3760                 ja pi_err
3761                 mov cl,8                ; Base = octal
3762                 jmp short pi_foundbase
3763 pi_ishex:
3764                 mov al,'0'              ; No numeric value accrued yet
3765                 mov cl,16               ; Base = hex
3766 pi_foundbase:
3767                 call unhexchar
3768                 jc pi_km                ; Not a (hex) digit
3769                 cmp al,cl
3770                 jae pi_km               ; Invalid for base
3771                 imul ebx,ecx            ; Multiply accumulated by base
3772                 add ebx,eax             ; Add current digit
3773                 lodsb
3774                 jmp short pi_foundbase
3775 pi_km:
3776                 dec si                  ; Back up to last non-numeric
3777                 lodsb
3778                 or al,20h
3779                 cmp al,'k'
3780                 je pi_isk
3781                 cmp al,'m'
3782                 je pi_ism
3783                 dec si                  ; Back up
3784 pi_fini:        and bp,bp
3785                 jz pi_ret               ; CF=0!
3786                 neg ebx                 ; Value was negative
3787 pi_done:        clc
3788 pi_ret:         pop bp
3789                 pop ecx
3790                 pop eax
3791                 ret
3792 pi_err:         stc
3793                 jmp short pi_ret
3794 pi_isk:         shl ebx,10              ; x 2^10
3795                 jmp short pi_done
3796 pi_ism:         shl ebx,20              ; x 2^20
3797                 jmp short pi_done
3798
3799 ;
3800 ; unhexchar:    Convert a hexadecimal digit in AL to the equivalent number;
3801 ;               return CF=1 if not a hex digit
3802 ;
3803 unhexchar:
3804                 cmp al,'0'
3805                 jb uxc_ret              ; If failure, CF == 1 already
3806                 cmp al,'9'
3807                 ja uxc_1
3808                 sub al,'0'              ; CF <- 0
3809                 ret
3810 uxc_1:          or al,20h               ; upper case -> lower case
3811                 cmp al,'a'
3812                 jb uxc_ret              ; If failure, CF == 1 already
3813                 cmp al,'f'
3814                 ja uxc_err
3815                 sub al,'a'-10           ; CF <- 0
3816                 ret
3817 uxc_err:        stc
3818 uxc_ret:        ret
3819
3820 ;
3821 ;
3822 ; getline:      Get a command line, converting control characters to spaces
3823 ;               and collapsing streches to one; a space is appended to the
3824 ;               end of the string, unless the line is empty.
3825 ;               The line is terminated by ^J, ^Z or EOF and is written
3826 ;               to ES:DI.  On return, DI points to first char after string.
3827 ;               CF is set if we hit EOF.
3828 ;
3829 getline:
3830                 call skipspace
3831                 mov dl,1                ; Empty line -> empty string.
3832                 jz gl_eof               ; eof
3833                 jc gl_eoln              ; eoln
3834                 call ungetc
3835 gl_fillloop:    push dx
3836                 push di
3837                 call getc
3838                 pop di
3839                 pop dx
3840                 jc gl_ret               ; CF set!
3841                 cmp al,' '
3842                 jna gl_ctrl
3843                 xor dx,dx
3844 gl_store:       stosb
3845                 jmp short gl_fillloop
3846 gl_ctrl:        cmp al,10
3847                 je gl_ret               ; CF clear!
3848                 cmp al,26
3849                 je gl_eof
3850                 and dl,dl
3851                 jnz gl_fillloop         ; Ignore multiple spaces
3852                 mov al,' '              ; Ctrl -> space
3853                 inc dx
3854                 jmp short gl_store
3855 gl_eoln:        clc                     ; End of line is not end of file
3856                 jmp short gl_ret
3857 gl_eof:         stc
3858 gl_ret:         pushf                   ; We want the last char to be space!
3859                 and dl,dl
3860                 jnz gl_xret
3861                 mov al,' '
3862                 stosb
3863 gl_xret:        popf
3864                 ret
3865
3866
3867 ;
3868 ; mangle_name: Mangle a DOS filename pointed to by DS:SI into a buffer pointed
3869 ;              to by ES:DI; ends on encountering any whitespace
3870 ;
3871
3872 mangle_name:
3873                 mov cx,11                       ; # of bytes to write
3874 mn_loop:
3875                 lodsb
3876                 cmp al,' '                      ; If control or space, end
3877                 jna mn_end
3878                 cmp al,'.'                      ; Period -> space-fill
3879                 je mn_is_period
3880                 cmp al,'a'
3881                 jb mn_not_lower
3882                 cmp al,'z'
3883                 ja mn_not_uslower
3884                 sub al,020h
3885                 jmp short mn_not_lower
3886 mn_is_period:   mov al,' '                      ; We need to space-fill
3887 mn_period_loop: cmp cx,3                        ; If <= 3 characters left
3888                 jbe mn_loop                     ; Just ignore it
3889                 stosb                           ; Otherwise, write a period
3890                 loop mn_period_loop             ; Dec CX and (always) jump
3891 mn_not_uslower: cmp al,ucase_low
3892                 jb mn_not_lower
3893                 cmp al,ucase_high
3894                 ja mn_not_lower
3895                 mov bx,ucase_tab-ucase_low
3896                 cs xlatb
3897 mn_not_lower:   stosb
3898                 loop mn_loop                    ; Don't continue if too long
3899 mn_end:
3900                 mov al,' '                      ; Space-fill name
3901                 rep stosb                       ; Doesn't do anything if CX=0
3902                 ret                             ; Done
3903
3904 ;
3905 ; Upper-case table for extended characters; this is technically code page 865,
3906 ; but code page 437 users will probably not miss not being able to use the
3907 ; cent sign in kernel images too much :-)
3908 ;
3909 ; The table only covers the range 129 to 164; the rest we can deal with.
3910 ;
3911 ucase_low       equ 129
3912 ucase_high      equ 164
3913 ucase_tab       db 154, 144, 'A', 142, 'A', 143, 128, 'EEEIII'
3914                 db 142, 143, 144, 146, 146, 'O', 153, 'OUUY', 153, 154
3915                 db 157, 156, 157, 158, 159, 'AIOU', 165
3916
3917 ;
3918 ; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
3919 ;                filename to the conventional representation.  This is needed
3920 ;                for the BOOT_IMAGE= parameter for the kernel.
3921 ;                NOTE: A 13-byte buffer is mandatory, even if the string is
3922 ;                known to be shorter.
3923 ;
3924 ;                DS:SI -> input mangled file name
3925 ;                ES:DI -> output buffer
3926 ;
3927 ;                On return, DI points to the first byte after the output name,
3928 ;                which is set to a null byte.
3929 ;
3930 unmangle_name:
3931                 push si                 ; Save pointer to original name
3932                 mov cx,8
3933                 mov bp,di
3934 un_copy_body:   lodsb
3935                 call lower_case
3936                 stosb
3937                 cmp al,' '
3938                 jbe un_cb_space
3939                 mov bp,di               ; Position of last nonblank+1
3940 un_cb_space:    loop un_copy_body
3941                 mov di,bp
3942                 mov al,'.'              ; Don't save
3943                 stosb
3944                 mov cx,3
3945 un_copy_ext:    lodsb
3946                 call lower_case
3947                 stosb
3948                 cmp al,' '
3949                 jbe un_ce_space
3950                 mov bp,di
3951 un_ce_space:    loop un_copy_ext
3952                 mov di,bp
3953                 mov byte [es:di], 0
3954                 pop si
3955                 ret
3956
3957 ;
3958 ; lower_case: Lower case a character in AL
3959 ;
3960 lower_case:
3961                 cmp al,'A'
3962                 jb lc_ret
3963                 cmp al,'Z'
3964                 ja lc_1
3965                 or al,20h
3966                 ret
3967 lc_1:           cmp al,lcase_low
3968                 jb lc_ret
3969                 cmp al,lcase_high
3970                 ja lc_ret
3971                 push bx
3972                 mov bx,lcase_tab-lcase_low
3973                 cs xlatb
3974                 pop bx
3975 lc_ret:         ret
3976
3977 ; ----------------------------------------------------------------------------------
3978 ;  VGA splash screen code
3979 ; ----------------------------------------------------------------------------------
3980
3981 ;
3982 ; vgadisplayfile:
3983 ;       Display a graphical splash screen.
3984 ;
3985 ; Input:
3986 ;
3987 ; SI    = cluster/socket pointer
3988 ;
3989 vgadisplayfile:
3990                 mov [VGACluster],si
3991                 push es
3992
3993                 ; This is a cheap and easy way to make sure the screen is
3994                 ; cleared in case we were in graphics mode already
3995                 call vgaclearmode
3996                 call vgasetmode
3997                 jnz .error_nz
3998
3999 .graphalready:
4000                 mov ax,xfer_buf_seg             ; Use as temporary storage
4001                 mov es,ax
4002                 mov fs,ax
4003
4004                 call vgagetchunk                ; Get the first chunk
4005
4006                 ; The header WILL be in the first chunk.
4007                 cmp dword [es:xbs_vgabuf],0x1413f33d    ; Magic number
4008 .error_nz:      jne near .error
4009                 mov ax,[es:xbs_vgabuf+4]
4010                 mov [GraphXSize],ax
4011
4012                 mov dx,xbs_vgabuf+8             ; Color map offset
4013                 mov ax,1012h                    ; Set RGB registers
4014                 xor bx,bx                       ; First register number
4015                 mov cx,16                       ; 16 registers
4016                 int 10h
4017         
4018 .movecursor:
4019                 mov ax,[es:xbs_vgabuf+6]        ; Number of pixel rows
4020                 mov dx,[VGAFontSize]
4021                 add ax,dx
4022                 dec ax
4023                 div dl
4024                 xor dx,dx                       ; Set column to 0
4025                 cmp al,[VidRows]
4026                 jb .rowsok
4027                 mov al,[VidRows]
4028                 dec al
4029 .rowsok:
4030                 mov dh,al
4031                 mov ah,2
4032                 xor bx,bx
4033                 int 10h                         ; Set cursor below image
4034
4035                 mov cx,[es:xbs_vgabuf+6]        ; Number of graphics rows
4036
4037                 mov si,xbs_vgabuf+8+3*16        ; Beginning of pixel data
4038                 mov word [VGAPos],0
4039
4040 .drawpixelrow:
4041                 push cx
4042                 mov cx,[GraphXSize]
4043                 mov di,xbs_vgatmpbuf            ; Row buffer
4044                 call rledecode                  ; Decode one row
4045                 push si
4046                 mov si,xbs_vgatmpbuf
4047                 mov di,si
4048                 add di,[GraphXSize]
4049                 mov cx,640/4
4050                 xor eax,eax
4051                 rep stosd                       ; Clear rest of row
4052                 mov di,0A000h                   ; VGA segment
4053                 mov es,di
4054                 mov di,[VGAPos]
4055                 mov bp,640
4056                 call packedpixel2vga
4057                 add word [VGAPos],byte 80       ; Advance to next pixel row
4058                 push fs
4059                 pop es
4060                 pop si
4061                 pop cx
4062                 loop .drawpixelrow
4063
4064 .error:
4065                 pop es
4066                 ret
4067
4068 ;
4069 ; rledecode:
4070 ;       Decode a pixel row in RLE16 format.
4071 ;
4072 ; FS:SI -> input
4073 ; CX -> pixel count
4074 ; ES:DI -> output (packed pixel)
4075 ;
4076 rledecode:
4077                 shl esi,1               ; Nybble pointer
4078                 xor dl,dl               ; Last pixel
4079 .loop:
4080                 call .getnybble
4081                 cmp al,dl
4082                 je .run                 ; Start of run sequence
4083                 stosb
4084                 mov dl,al
4085                 dec cx
4086                 jnz .loop
4087 .done:
4088                 shr esi,1
4089                 adc si,byte 0
4090                 ret
4091 .run:
4092                 xor bx,bx
4093                 call .getnybble
4094                 and al,al
4095                 jz .longrun
4096                 mov bl,al
4097 .dorun:
4098                 push cx
4099                 mov cx,bx
4100                 mov al,dl
4101                 rep stosb
4102                 pop cx
4103                 sub cx,bx
4104                 ja .loop
4105                 jmp short .done
4106 .longrun:
4107                 call .getnybble
4108                 mov ah,al
4109                 call .getnybble
4110                 shl al,4
4111                 or al,ah
4112                 mov bl,al
4113                 add bx,16
4114                 jmp short .dorun
4115 .getnybble:
4116                 shr esi,1
4117                 fs lodsb
4118                 jc .high
4119                 dec si
4120                 and al,0Fh
4121                 stc
4122                 rcl esi,1
4123                 ret
4124 .high:
4125                 shr al,4
4126                 cmp si,xbs_vgabuf+trackbufsize  ; Chunk overrun
4127                 jb .nonewchunk
4128                 call vgagetchunk
4129                 mov si,xbs_vgabuf               ; Start at beginning of buffer
4130 .nonewchunk:
4131                 shl esi,1
4132                 ret
4133
4134 ;
4135 ; vgagetchunk:
4136 ;       Get a new trackbufsize chunk of VGA image data
4137 ;
4138 ; On input, ES is assumed to point to the buffer segment.
4139 ;
4140 vgagetchunk:
4141                 pushad
4142                 mov si,[VGACluster]
4143                 and si,si
4144                 jz .eof                         ; EOF overrun, not much to do...
4145
4146                 mov cx,[BufSafe]                ; One trackbuf worth of data
4147                 mov bx,xbs_vgabuf
4148                 call getfssec
4149
4150                 jnc .noteof
4151                 xor si,si
4152 .noteof:        mov [VGACluster],si
4153
4154 .eof:           popad
4155                 ret
4156
4157 ;
4158 ; packedpixel2vga:
4159 ;       Convert packed-pixel to VGA bitplanes
4160 ;
4161 ; FS:SI -> packed pixel string
4162 ; BP    -> pixel count (multiple of 8)
4163 ; ES:DI -> output
4164 ;
4165 packedpixel2vga:
4166                 mov dx,3C4h     ; VGA Sequencer Register select port
4167                 mov al,2        ; Sequencer mask
4168                 out dx,al       ; Select the sequencer mask
4169                 inc dx          ; VGA Sequencer Register data port
4170                 mov al,1
4171                 mov bl,al
4172 .planeloop:
4173                 pusha
4174                 out dx,al
4175 .loop1:
4176                 mov cx,8
4177 .loop2:
4178                 xchg cx,bx
4179                 fs lodsb
4180                 shr al,cl
4181                 rcl ch,1        ; VGA is bigendian.  Sigh.
4182                 xchg cx,bx
4183                 loop .loop2
4184                 mov al,bh
4185                 stosb
4186                 sub bp,byte 8
4187                 ja .loop1
4188                 popa
4189                 inc bl
4190                 shl al,1
4191                 cmp bl,4
4192                 jbe .planeloop
4193                 ret
4194
4195 ;
4196 ; vgasetmode:
4197 ;       Enable VGA graphics, if possible; return ZF=1 on success
4198 ;       DS must be set to the base segment; ES is set to DS.
4199 ;
4200 vgasetmode:
4201                 push ds
4202                 pop es
4203                 mov ax,1A00h            ; Get video card and monitor
4204                 xor bx,bx
4205                 int 10h
4206                 sub bl, 7               ; BL=07h and BL=08h OK
4207                 cmp bl, 1
4208                 ja .error               ; ZF=0
4209 ;               mov bx,TextColorReg
4210 ;               mov dx,1009h            ; Read color registers
4211 ;               int 10h
4212                 mov ax,0012h            ; Set mode = 640x480 VGA 16 colors
4213                 int 10h
4214                 mov dx,linear_color
4215                 mov ax,1002h            ; Write color registers
4216                 int 10h
4217                 mov [UsingVGA], byte 1
4218
4219                 call use_font           ; Set graphics font/data
4220                 mov byte [ScrollAttribute], 00h
4221
4222                 xor ax,ax               ; Set ZF
4223 .error:
4224                 ret
4225
4226 ;
4227 ; vgaclearmode:
4228 ;       Disable VGA graphics.  It is not safe to assume any value
4229 ;       for DS or ES.
4230 ;
4231 vgaclearmode:
4232                 push ds
4233                 push es
4234                 pushad
4235                 mov ax,cs
4236                 mov ds,ax
4237                 mov es,ax
4238                 cmp [UsingVGA], byte 1
4239                 jne .done
4240                 mov ax,0003h            ; Return to normal video mode
4241                 int 10h
4242 ;               mov dx,TextColorReg     ; Restore color registers
4243 ;               mov ax,1002h
4244 ;               int 10h
4245                 mov [UsingVGA], byte 0
4246
4247                 call use_font           ; Restore text font/data
4248                 mov byte [ScrollAttribute], 07h
4249 .done:
4250                 popad
4251                 pop es
4252                 pop ds
4253                 ret
4254
4255 ;
4256 ; vgashowcursor/vgahidecursor:
4257 ;       If VGA graphics is enabled, draw a cursor/clear a cursor
4258 ;
4259 vgashowcursor:
4260                 pushad
4261                 mov al,'_'
4262                 jmp short vgacursorcommon
4263 vgahidecursor:
4264                 pushad
4265                 mov al,' '
4266 vgacursorcommon:
4267                 cmp [UsingVGA], byte 1
4268                 jne .done
4269                 mov ah,09h
4270                 mov bx,0007h
4271                 mov cx,1
4272                 int 10h
4273 .done:
4274                 popad
4275                 ret
4276
4277
4278                 ; Map colors to consecutive DAC registers
4279 linear_color    db 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0
4280 UsingVGA        db 0
4281
4282 ; ----------------------------------------------------------------------------------
4283 ;  Begin data section
4284 ; ----------------------------------------------------------------------------------
4285
4286 CR              equ 13          ; Carriage Return
4287 LF              equ 10          ; Line Feed
4288 FF              equ 12          ; Form Feed
4289 BS              equ  8          ; Backspace
4290
4291 ;
4292 ; Lower-case table for codepage 865
4293 ;
4294 lcase_low       equ 128
4295 lcase_high      equ 165
4296 lcase_tab       db 135, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138
4297                 db 139, 140, 141, 132, 134, 130, 145, 145, 147, 148, 149
4298                 db 150, 151, 152, 148, 129, 155, 156, 155, 158, 159, 160
4299                 db 161, 162, 163, 164, 164
4300
4301 copyright_str   db ' Copyright (C) 1994-', year, ' H. Peter Anvin'
4302                 db CR, LF, 0
4303 boot_prompt     db 'boot: ', 0
4304 wipe_char       db BS, ' ', BS, 0
4305 err_notfound    db 'Could not find kernel image: ',0
4306 err_notkernel   db CR, LF, 'Invalid or corrupt kernel image.', CR, LF, 0
4307 err_not386      db 'It appears your computer uses a 286 or lower CPU.'
4308                 db CR, LF
4309                 db 'You cannot run Linux unless you have a 386 or higher CPU'
4310                 db CR, LF
4311                 db 'in your machine.  If you get this message in error, hold'
4312                 db CR, LF
4313                 db 'down the Ctrl key while booting, and I will take your'
4314                 db CR, LF
4315                 db 'word for it.', CR, LF, 0
4316 err_noram       db 'It appears your computer has less than 488K of low ("DOS")'
4317                 db CR, LF
4318                 db 'RAM.  Linux needs at least this amount to boot.  If you get'
4319                 db CR, LF
4320                 db 'this message in error, hold down the Ctrl key while'
4321                 db CR, LF
4322                 db 'booting, and I will take your word for it.', CR, LF, 0
4323 err_badcfg      db 'Unknown keyword in syslinux.cfg.', CR, LF, 0
4324 err_noparm      db 'Missing parameter in syslinux.cfg.', CR, LF, 0
4325 err_noinitrd    db CR, LF, 'Could not find ramdisk image: ', 0
4326 err_nohighmem   db 'Not enough memory to load specified kernel.', CR, LF, 0
4327 err_highload    db CR, LF, 'Kernel transfer failure.', CR, LF, 0
4328 err_oldkernel   db 'Cannot load a ramdisk with an old kernel image.'
4329                 db CR, LF, 0
4330 err_notdos      db ': attempted DOS system call', CR, LF, 0
4331 err_comlarge    db 'COMBOOT image too large.', CR, LF, 0
4332 err_bootsec     db 'Invalid or corrupt boot sector image.', CR, LF, 0
4333 err_a20         db CR, LF, 'A20 gate not responding!', CR, LF, 0
4334 err_bootfailed  db CR, LF, 'Boot failed: please change disks and press '
4335                 db 'a key to continue.', CR, LF, 0
4336 ready_msg       db 'Ready.', CR, LF, 0
4337 crlfloading_msg db CR, LF
4338 loading_msg     db 'Loading ', 0
4339 dotdot_msg      db '.'
4340 dot_msg         db '.', 0
4341 aborted_msg     db ' aborted.'                  ; Fall through to crlf_msg!
4342 crlf_msg        db CR, LF, 0
4343 crff_msg        db CR, FF, 0
4344 syslinux_cfg    db 'SYSLINUXCFG'
4345 ;
4346 ; Command line options we'd like to take a look at
4347 ;
4348 ; mem= and vga= are handled as normal 32-bit integer values
4349 initrd_cmd      db 'initrd='
4350 initrd_cmd_len  equ 7
4351 ;
4352 ; Config file keyword table
4353 ;
4354                 align 2, db 0
4355 keywd_table     db 'ap' ; append
4356                 db 'de' ; default
4357                 db 'ti' ; timeout
4358                 db 'fo' ; font
4359                 db 'kb' ; kbd
4360                 db 'di' ; display
4361                 db 'pr' ; prompt
4362                 db 'la' ; label
4363                 db 'im' ; implicit
4364                 db 'ke' ; kernel
4365                 db 'se' ; serial
4366                 db 'f1' ; F1
4367                 db 'f2' ; F2
4368                 db 'f3' ; F3
4369                 db 'f4' ; F4
4370                 db 'f5' ; F5
4371                 db 'f6' ; F6
4372                 db 'f7' ; F7
4373                 db 'f8' ; F8
4374                 db 'f9' ; F9
4375                 db 'f0' ; F10
4376                 dw 0
4377 ;
4378 ; Extensions to search for (in *reverse* order).  Note that the last
4379 ; (lexically first) entry in the table is a placeholder for the original
4380 ; extension, needed for error messages.  The exten_table is shifted so
4381 ; the table is 1-based; this is because a "loop" cx is used as index.
4382 ;
4383 exten_table:
4384 OrigKernelExt:  dd 0                    ; Original extension
4385                 db 'COM',0              ; COMBOOT (same as DOS)
4386                 db 'BS ',0              ; Boot Sector 
4387                 db 'BSS',0              ; Boot Sector (add superblock)
4388                 db 'CBT',0              ; COMBOOT (specific)
4389
4390 exten_count     equ (($-exten_table) >> 2) - 1  ; Number of alternates
4391 ;
4392 ; Misc initialized (data) variables
4393 ;
4394 %ifdef debug                            ; This code for debugging only
4395 debug_magic     dw 0D00Dh               ; Debug code sentinel
4396 %endif
4397 AppendLen       dw 0                    ; Bytes in append= command
4398 KbdTimeOut      dw 0                    ; Keyboard timeout (if any)
4399 FKeyMap         dw 0                    ; Bitmap for F-keys loaded
4400 CmdLinePtr      dw cmd_line_here        ; Command line advancing pointer
4401 initrd_flag     equ $
4402 initrd_ptr      dw 0                    ; Initial ramdisk pointer/flag
4403 VKernelCtr      dw 0                    ; Number of registered vkernels
4404 ForcePrompt     dw 0                    ; Force prompt
4405 AllowImplicit   dw 1                    ; Allow implicit kernels
4406 SerialPort      dw 0                    ; Serial port base (or 0 for no serial port)
4407 A20List         dw a20_dunno, a20_none, a20_bios, a20_kbc, a20_fast
4408 A20DList        dw a20d_dunno, a20d_none, a20d_bios, a20d_kbc, a20d_fast
4409 A20Type         dw A20_DUNNO            ; A20 type unknown
4410 VGAFontSize     dw 16                   ; Defaults to 16 byte font
4411 UserFont        db 0                    ; Using a user-specified font
4412 ScrollAttribute db 07h                  ; White on black (for text mode)
4413 ;
4414 ; Stuff for the command line; we do some trickery here with equ to avoid
4415 ; tons of zeros appended to our file and wasting space
4416 ;
4417 linuxauto_cmd   db 'linux auto',0
4418 linuxauto_len   equ $-linuxauto_cmd
4419 boot_image      db 'BOOT_IMAGE='
4420 boot_image_len  equ $-boot_image
4421                 align 4, db 0           ; For the good of REP MOVSD
4422 command_line    equ $
4423 default_cmd     equ $+(max_cmd_len+2)
4424 ldlinux_end     equ default_cmd+(max_cmd_len+1)
4425 kern_cmd_len    equ ldlinux_end-command_line
4426 ldlinux_len     equ ldlinux_end-ldlinux_magic
4427 ;
4428 ; Put the getcbuf right after the code, aligned on a sector boundary
4429 ;
4430 end_of_code     equ (ldlinux_end-bootsec)+7C00h
4431 getcbuf         equ (end_of_code + 511) & 0FE00h
4432
4433 ; VGA font buffer at the end of memory (so loading a font works even
4434 ; in graphics mode.)
4435 vgafontbuf      equ 0E000h
4436
4437 ; This is a compile-time assert that we didn't run out of space
4438 %if (getcbuf+trackbufsize) > vgafontbuf
4439 %error "Out of memory, better reorganize something..."
4440 %endif