1 ; -*- fundamental -*- (asm-mode sucks)
2 ; ****************************************************************************
6 ; A program to boot Linux kernels off an MS-DOS formatted floppy disk. This
7 ; functionality is good to have for installation floppies, where it may
8 ; be hard to find a functional Linux system to run LILO off.
10 ; This program allows manipulation of the disk to take place entirely
11 ; from MS-LOSS, and can be especially useful in conjunction with the
14 ; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved
15 ; Copyright 2009 Intel Corporation; author: H. Peter Anvin
17 ; This program is free software; you can redistribute it and/or modify
18 ; it under the terms of the GNU General Public License as published by
19 ; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
20 ; Boston MA 02111-1307, USA; either version 2 of the License, or
21 ; (at your option) any later version; incorporated herein by reference.
23 ; ****************************************************************************
31 ; Some semi-configurable constants... change on your own risk.
34 FILENAME_MAX_LG2 equ 6 ; log2(Max filename size Including final null)
35 FILENAME_MAX equ (1<<FILENAME_MAX_LG2) ; Max mangled filename size
36 NULLFILE equ 0 ; First char space == null filename
37 NULLOFFSET equ 0 ; Position in which to look
38 retry_count equ 16 ; How patient are we with the disk?
39 %assign HIGHMEM_SLOP 0 ; Avoid this much memory near the top
40 LDLINUX_MAGIC equ 0x3eb202fe ; A random number to identify ourselves with
42 MAX_OPEN_LG2 equ 6 ; log2(Max number of open files)
43 MAX_OPEN equ (1 << MAX_OPEN_LG2)
46 SECTOR_SIZE equ (1 << SECTOR_SHIFT)
49 DIRENT_SIZE equ (1 << DIRENT_SHIFT)
51 ROOT_DIR_WORD equ 0x002F
54 ; The following structure is used for "virtual kernels"; i.e. LILO-style
55 ; option labels. The options we permit here are `kernel' and `append
56 ; Since there is no room in the bottom 64K for all of these, we
57 ; stick them in high memory and copy them down before we need them.
60 vk_vname: resb FILENAME_MAX ; Virtual name **MUST BE FIRST!**
61 vk_rname: resb FILENAME_MAX ; Real name
63 vk_type: resb 1 ; Type of file
65 vk_append: resb max_cmd_len+1 ; Command line
67 vk_end: equ $ ; Should be <= vk_size
71 ; File structure. This holds the information for each currently open file.
74 file_sector resd 1 ; Sector pointer (0 = structure free)
75 file_bytesleft resd 1 ; Number of bytes left
76 file_left resd 1 ; Number of sectors left
81 ; Structure for codepage files
84 .magic resd 2 ; 8-byte magic number
85 .reserved resd 6 ; Reserved for future use
86 .uppercase resb 256 ; Internal upper-case table
87 .unicode resw 256 ; Unicode matching table
88 .unicode_alt resw 256 ; Alternate Unicode matching table
92 %if (open_file_t_size & (open_file_t_size-1))
93 %error "open_file_t is not a power of 2"
97 ; ---------------------------------------------------------------------------
99 ; ---------------------------------------------------------------------------
102 ; Memory below this point is reserved for the BIOS and the MBR
105 trackbufsize equ 8192
106 trackbuf resb trackbufsize ; Track buffer goes here
111 FAT resd 1 ; Location of (first) FAT
112 RootDirArea resd 1 ; Location of root directory area
113 RootDir resd 1 ; Location of root directory proper
114 DataArea resd 1 ; Location of data area
115 RootDirSize resd 1 ; Root dir size in sectors
116 TotalSectors resd 1 ; Total number of sectors
117 ClustSize resd 1 ; Bytes/cluster
118 ClustMask resd 1 ; Sectors/cluster - 1
119 Clusters resd 1 ; Total number of clusters
120 CopySuper resb 1 ; Distinguish .bs versus .bss
121 DriveNumber resb 1 ; BIOS drive number
122 ClustShift resb 1 ; Shift count for sectors/cluster
123 ClustByteShift resb 1 ; Shift count for bytes/cluster
125 alignb open_file_t_size
126 Files resb MAX_OPEN*open_file_t_size
129 ; Common bootstrap code for disk-based derivatives
131 %include "diskstart.inc"
134 ; Compute some information about this filesystem.
137 ; First, generate the map of regions
142 mov edx,[bsHugeSectors]
144 mov [TotalSectors],edx
146 mov eax,[bxResSectors]
147 mov [FAT],eax ; Beginning of FAT
151 mov edx,[bootsec+36] ; FAT32 BPB_FATsz32
155 mov [RootDirArea],eax ; Beginning of root directory
156 mov [RootDir],eax ; For FAT12/16 == root dir location
158 mov edx,[bxRootDirEnts]
159 add dx,SECTOR_SIZE/32-1
160 shr dx,SECTOR_SHIFT-5
161 mov [RootDirSize],edx
163 mov [DataArea],eax ; Beginning of data area
165 ; Next, generate a cluster size shift count and mask
166 mov eax,[bxSecPerClust]
171 mov [ClustByteShift],cl
180 ; FAT12, FAT16 or FAT28^H^H32? This computation is fscking ridiculous.
183 mov eax,[TotalSectors]
185 shr eax,cl ; cl == ClustShift
186 mov cl,nextcluster_fat12-(nextcluster+2)
187 cmp eax,0xFF4 ; FAT12 limit
189 mov cl,nextcluster_fat16-(nextcluster+2)
190 cmp eax,0xFFF4 ; FAT16 limit
193 ; FAT32, root directory is a cluster chain
195 mov ecx,0x0FFFFFF4 ; Max possible cluster count
202 mov edx,[bootsec+44] ; Root directory cluster
207 mov cl,nextcluster_fat28-(nextcluster+2)
208 mov byte [SuperSize],superblock_len_fat32
210 mov byte [nextcluster+1],cl
211 mov [Clusters],eax ; Total clusters
214 ; Common initialization code
216 %include "cpuinit.inc"
220 ; Initialize the metadata cache
225 ; Now, everything is "up and running"... patch kaboom for more
226 ; verbosity and using the full screen system
229 mov dword [kaboom.patch],0e9h+((kaboom2-(kaboom.patch+3)) << 8)
232 ; Now we're all set to start with our *real* business. First load the
233 ; configuration file (if any) and parse it.
235 ; In previous versions I avoided using 32-bit registers because of a
236 ; rumour some BIOSes clobbered the upper half of 32-bit registers at
237 ; random. I figure, though, that if there are any of those still left
238 ; they probably won't be trying to install Linux on them...
240 ; The code is still ripe with 16-bitisms, though. Not worth the hassle
241 ; to take'm out. In fact, we may want to put them back if we're going
242 ; to boot ELKS at some point.
246 ; Load configuration file
248 mov si,config_name ; Save configuration file name
251 mov word [CurrentDirName],ROOT_DIR_WORD ; Write '/',0 to the CurrentDirName
253 mov eax,[RootDir] ; Make the root directory ...
254 mov [CurrentDir],eax ; ... the current directory
274 mov di,CurrentDirName
275 ; This is inefficient as it will copy more than needed
276 ; but not by too much
278 mov ax,config_name ;Cut it down
281 mov di,CurrentDirName
285 mov eax,[PrevDir] ; Make the directory with syslinux.cfg ...
286 mov [CurrentDir],eax ; ... the current directory
289 ; Now we have the config file open. Parse the config file and
290 ; run the user interface.
295 ; allocate_file: Allocate a file structure
308 .check: cmp dword [bx], byte 0
310 add bx,open_file_t_size ; ZF = 0
312 ; ZF = 0 if we fell out of the loop
318 ; Allocate then fill a file structure for a directory starting in
321 ; Assumes DS == ES == CS.
336 mov [si+file_sector],eax ; Current sector
337 mov dword [si+file_bytesleft],0 ; Current offset
338 mov [si+file_left],eax ; Beginning sector
344 xor eax,eax ; ZF <- 1
349 ; Search a specific directory for a pre-mangled filename in
350 ; MangledBuf, in the directory starting in sector EAX.
352 ; NOTE: This file considers finding a zero-length file an
353 ; error. This is so we don't have to deal with that special
354 ; case elsewhere in the program (most loops have the test
357 ; Assumes DS == ES == CS.
362 ; EAX = file length (MAY BE ZERO!)
363 ; DL = file attribute
367 ; EAX, SI, DX clobbered
381 ; Compute the value of a possible VFAT longname
382 ; "last" entry (which, of course, comes first...)
397 ; EAX <- directory sector to scan
399 ; GS:SI now points to this sector
401 mov cx,SECTOR_SIZE/32 ; 32 == directory entry size
404 jz .failure ; Hit directory high water mark
405 cmp word [gs:si+11],0Fh ; Long filename
408 ; Process a VFAT long entry
416 ; Get the initial checksum value
421 jne .not_us ; Checksum mismatch
424 jz .not_us ; Can't be zero...
426 mov [VFATNext],al ; Optimistically...
442 movzx bx,byte [bx+di]
444 cmp ax,[cp_unicode+bx] ; Primary case
446 cmp ax,[cp_unicode_alt+bx] ; Alternate case
451 ; *AT* the end we should have 0x0000, *AFTER* the end
452 ; we should have 0xFFFF...
454 inc ax ; 0xFFFF -> 0x0000
465 .vfat_adj_add3: inc si
466 .vfat_adj_add2: inc si
467 .vfat_adj_add1: inc si
470 ; Okay, if we got here we had a match on this particular
471 ; entry... live to see another one.
482 test byte [gs:si+11],8 ; Ignore volume labels
485 cmp byte [VFATNext],0 ; Do we have a longname match?
488 ; We already have a VFAT longname match, however,
489 ; the match is only valid if the checksum matches
504 je .found ; Got a match on longname
506 .no_long_match: ; Look for a shortname match
518 ; Reset the VFAT matching state machine
527 jnc .scansector ; CF is set if we're at end
529 ; If we get here, we failed
536 xor eax,eax ; ZF <- 1
539 mov eax,[gs:si+28] ; File size
540 add eax,SECTOR_SIZE-1
542 mov [bx+4],eax ; Sector count
545 mov dx,[gs:si+20] ; High cluster word
547 mov dx,[gs:si+26] ; Low cluster word
551 mov [bx],edx ; Starting sector
553 mov eax,[gs:si+28] ; File length again
554 mov dl,[gs:si+11] ; File attribute
555 mov si,bx ; File pointer...
566 ; Note: we have no use of the first 32 bytes (header),
567 ; nor of the folloing 32 bytes (case mapping of control
568 ; characters), as long as we adjust the offsets appropriately.
569 codepage equ $-(32+32)
570 codepage_data: incbin "codepage.cp",32+32
571 cp_uppercase equ codepage+cp.uppercase
572 cp_unicode equ codepage+cp.unicode
573 cp_unicode_alt equ codepage+cp.unicode_alt
578 ; Input: UCS-2 character in AX
579 ; Output: Single byte character in AL, ZF = 1
580 ; On failure, returns ZF = 0
595 not ax ; Doesn't change the flags!
606 ; Deallocates a file structure (pointer in SI)
612 mov dword [si],0 ; First dword == file_sector
618 ; Deallocates a directory structure (pointer in SI)
624 mov dword [si],0 ; First dword == file_sector
638 ; EAX = file length in bytes
642 ; Assumes CS == DS == ES, and trashes BX and CX.
646 cmp byte [di],'/' ; Root directory?
653 push eax ; <A> Current directory sector
662 xchg si,di ; GRC: si begin; di end[ /]+1
663 pop eax ; <A> Current directory sector
665 ; GRC Here I need to check if di-1 = si which signifies
666 ; we have the desired directory in EAX
667 ; What about where the file name = "."; later
673 mov [PrevDir],eax ; Remember last directory searched
676 call mangle_dos_name ; MangledBuf <- component
679 jz .notfound ; Pathname component missing
681 cmp byte [di-1],'/' ; Do we expect a directory
684 ; Otherwise, it should be a file
686 test dl,18h ; Subdirectory|Volume Label
687 jnz .badfile ; If not a file, it's a bad thing
689 ; SI and EAX are already set
690 mov [si+file_bytesleft],eax
692 add eax,SECTOR_SIZE-1
694 mov [si+file_left],eax ; Sectors left
696 and eax,eax ; EAX != 0
700 ; If we expected a directory, it better be one...
702 test dl,10h ; Subdirectory
706 xchg eax,[si+file_sector] ; Get sector number and free file structure
707 jmp .pathwalk ; Walk the next bit of the path
709 ; Found the desired directory; ZF set but EAX not 0
715 mov [si],eax ; Free file structure
718 xor eax,eax ; Zero out EAX
722 ; readdir: Read one file from a directory
724 ; ES:DI -> String buffer (filename)
725 ; DS:SI -> Pointer to open_file_t
726 ; DS Must be the SYSLINUX Data Segment
728 ; Returns the file's name in the filename string buffer
729 ; EAX returns the file size
730 ; EBX returns the beginning sector (currently without offsetting)
731 ; DL returns the file type
732 ; The directory handle's data is incremented to reflect a name read.
736 push bp ; Using bp to transfer between segment registers
739 push fs ; Using fs to store the current es (from COMBOOT)
746 mov eax,[ds:si+file_sector] ; Current sector
747 mov ebx,[ds:si+file_bytesleft] ; Current offset
753 add si,bx ; Resume last position in sector
754 mov ecx,SECTOR_SIZE ; 0 out high part
756 shr cx,5 ; Number of entries left
760 cmp word [gs:si+11],0Fh ; Long filename
768 .vfat_ln_info: ; Get info about the line that we're on
773 mov ah,1 ; On beginning line
776 .vfat_tail_ln: ; VFAT tail line processing (later in VFAT, head in name)
777 test al,80h ; Invalid data?
779 mov ah,0 ; Not on beginning line
781 jne .vfat_abort ; Is this the entry we need?
787 .vfat_ck_ln: ; Load this line's VFAT CheckSum
790 .vfat_cp_ln: ; Copy VFAT line
791 dec al ; Store the next line we need
792 mov dx,ax ; Use DX to store the progress
793 mov cx,13 ; 13 characters per VFAT DIRENT
795 mul cl ; Offset for DI
796 add di,ax ; Increment DI
797 inc si ; Align to the real characters
799 gs lodsw ; Unicode here!!
800 call ucs2_to_cp ; Convert to local codepage
801 jnz .vfat_abort ; Use short name if character not on codepage
802 stosb ; CAN NOT OVERRIDE es
804 jz .vfat_find_next ; Null-terminated string; don't process more
809 .vfat_adj_add3: inc si
810 .vfat_adj_add2: inc si
811 .vfat_adj_add1: inc si
814 cmp dh,1 ; Is this the first round?
816 .vfat_null_term: ; Need to null-terminate if first line as we rolled over the end
820 .vfat_find_next: ;Find the next part of the name
826 jz .vfat_find_info ; We're done with the name
831 jnc .vfat_entry ; CF is set if we're at end
833 .vfat_find_info: ; Fetch next entry for the size/"INode"
838 jnc .get_info ; CF is set if we're at end
840 .vfat_abort: ; Something went wrong, skip
848 test byte [gs:si+11],8 ; Ignore volume labels //HERE
850 mov edx,eax ;Save current sector
863 loop .short_file_loop
865 .short_skip_bs: ; skip blank spaces in FILENAME (before EXT)
886 mov al,0 ; Null-terminate the short strings
893 mov ebx,[gs:si+28] ; length
894 mov dl,[gs:si+11] ; type
900 jnc .store_sect ; CF is set if we're at end
908 jnc .scanentry ; CF is set if we're at end
916 mov [ds:si+file_sector],eax
917 mov eax,0 ; Now at beginning of new sector
924 pop si ; cx=num remain; SECTOR_SIZE-(cx*32)=cur pos
931 mov [ds:si+file_bytesleft],eax
932 ; "INode" number = ((CurSector-RootSector)*SECTOR_SIZE + Offset)/DIRENT_SIZE)
934 mov eax,[ds:si+file_sector]
940 xchg eax,ebx ; -> EBX=INode, EAX=FileSize
959 CurrentDir resd 1 ; Current directory
960 PrevDir resd 1 ; Last scanned directory
966 ; kaboom2: once everything is loaded, replace the part of kaboom
967 ; starting with "kaboom.patch" with this part
970 mov si,err_bootfailed
972 cmp byte [kaboom.again+1],18h ; INT 18h version?
976 int 19h ; And try once more to boot...
977 .norge: jmp short .norge ; If int 19h returned; this is the end
981 .noreg: jmp short .noreg ; Nynorsk
984 ; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed
985 ; to by ES:DI; ends on encountering any whitespace.
988 ; This verifies that a filename is < FILENAME_MAX characters,
989 ; doesn't contain whitespace, zero-pads the output buffer,
990 ; and removes trailing dots and redundant slashes, plus changes
991 ; backslashes to forward slashes,
992 ; so "repe cmpsb" can do a compare, and the path-searching routine
993 ; gets a bit of an easier job.
1000 mov cx,FILENAME_MAX-1
1005 cmp al,' ' ; If control or space, end
1007 cmp al,'\' ; Backslash?
1009 mov al,'/' ; Change to forward slash
1011 cmp al,ah ; Repeated slash?
1018 .mn_skip: loop .mn_loop
1020 cmp bx,di ; At the beginning of the buffer?
1022 cmp byte [es:di-1],'.' ; Terminal dot?
1024 cmp byte [es:di-1],'/' ; Terminal slash?
1026 .mn_kill: dec di ; If so, remove it
1030 inc cx ; At least one null byte
1031 xor ax,ax ; Zero-fill name
1038 ; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
1039 ; filename to the conventional representation. This is needed
1040 ; for the BOOT_IMAGE= parameter for the kernel.
1041 ; NOTE: A 13-byte buffer is mandatory, even if the string is
1042 ; known to be shorter.
1044 ; DS:SI -> input mangled file name
1045 ; ES:DI -> output buffer
1047 ; On return, DI points to the first byte after the output name,
1048 ; which is set to a null byte.
1050 unmangle_name: call strcpy
1051 dec di ; Point to final null byte
1056 ; Mangle a DOS filename component pointed to by DS:SI
1057 ; into [MangledBuf]; ends on encountering any whitespace or
1060 ; WARNING: saves pointers into the buffer for longname
1063 ; Assumes CS == DS == ES.
1071 mov cx,11 ; # of bytes to write
1072 mov bx,cp_uppercase ; Case-conversion table
1075 cmp al,' ' ; If control or space, end
1077 cmp al,'/' ; Slash, too
1079 cmp al,'.' ; Period -> space-fill
1081 xlatb ; Convert to upper case
1082 mov ah,cl ; If the first byte (only!)...
1083 cmp ax,0BE5h ; ... equals E5 hex ...
1085 mov al,05h ; ... change it to 05 hex
1087 loop .loop ; Don't continue if too long
1088 ; Find the end for the benefit of longname search
1099 mov al,' ' ; Space-fill name
1100 rep stosb ; Doesn't do anything if CX=0
1105 mov al,' ' ; We need to space-fill
1106 .period_loop: cmp cx,3 ; If <= 3 characters left
1107 jbe .loop ; Just ignore it
1108 stosb ; Otherwise, write a space
1109 loop .period_loop ; Dec CX and *always* jump
1119 ; getfssec_edx: Get multiple sectors from a file
1121 ; This routine makes sure the subtransfers do not cross a 64K boundary,
1122 ; and will correct the situation if it does, UNLESS *sectors* cross
1126 ; EDX -> Current sector number
1127 ; CX -> Sector count (0FFFFh = until end of file)
1128 ; Must not exceed the ES segment
1129 ; Returns EDX=0, CF=1 on EOF (not necessarily error)
1130 ; All arguments are advanced to reflect data read.
1136 xor ebp,ebp ; Fragment sector count
1137 push edx ; Starting sector pointer
1145 add ax,bx ; Now AX = how far into 64K block we are
1146 not ax ; Bytes left in 64K block
1148 shr eax,SECTOR_SHIFT ; Sectors left in 64K block
1150 jnb .do_read ; Unless there is at least 1 more sector room...
1151 mov eax,edx ; Current sector
1152 inc edx ; Predict it's the linearly next sector
1155 cmp edx,eax ; Did it match?
1158 pop eax ; Starting sector pointer
1160 lea eax,[eax+ebp-1] ; This is the last sector actually read
1162 add bx,bp ; Adjust buffer pointer
1178 ; getfssec: Get multiple sectors from a file
1180 ; Same as above, except SI is a pointer to a open_file_t
1183 ; DS:SI -> Pointer to open_file_t
1184 ; CX -> Sector count (0FFFFh = until end of file)
1185 ; Must not exceed the ES segment
1186 ; Returns CF=1 on EOF (not necessarily error)
1187 ; ECX returns number of bytes read.
1188 ; All arguments are advanced to reflect data read.
1193 push edx ; Zero-extended CX
1194 cmp edx,[si+file_left]
1196 mov edx,[si+file_left]
1199 sub [si+file_left],edx
1200 mov edx,[si+file_sector]
1202 mov [si+file_sector],edx
1203 pop ecx ; Sectors requested read
1204 shl ecx,SECTOR_SHIFT
1205 cmp ecx,[si+file_bytesleft]
1208 sub [si+file_bytesleft],ecx ; CF <- 0
1212 mov ecx,[si+file_bytesleft]
1219 ; nextcluster: Advance a cluster pointer in EDI to the next cluster
1220 ; pointed at in the FAT tables. CF=0 on return if end of file.
1223 jmp strict short nextcluster_fat28 ; This gets patched
1233 pushf ; Save the shifted-out LSB (=CF)
1263 ; FAT16 decoding routine.
1270 shr eax,SECTOR_SHIFT-1
1275 movzx edi,word [gs:si+bx]
1283 ; FAT28 ("FAT32") decoding routine.
1290 shr eax,SECTOR_SHIFT-2
1296 mov edi,dword [gs:si+bx]
1297 and edi,0FFFFFFFh ; 28 bits only
1306 ; nextsector: Given a sector in EAX on input, return the next sector
1307 ; of the same filesystem object, which may be the root
1308 ; directory or a cluster chain. Returns EOF.
1328 test edi,[ClustMask]
1331 ; It's not the final sector in a cluster
1336 push gs ; nextcluster trashes gs
1343 ; Now EDI contains the cluster number
1346 jc .exit ; There isn't anything else...
1348 ; New cluster number now in EDI
1350 shl edi,cl ; CF <- 0, unless something is very wrong
1361 ; getfatsector: Check for a particular sector (in EAX) in the FAT cache,
1362 ; and return a pointer in GS:SI, loading it if needed.
1367 add eax,[FAT] ; FAT starting address
1370 ; -----------------------------------------------------------------------------
1372 ; -----------------------------------------------------------------------------
1374 %include "getc.inc" ; getc et al
1375 %include "conio.inc" ; Console I/O
1376 %include "plaincon.inc" ; writechr
1377 %include "writestr.inc" ; String output
1378 %include "writehex.inc" ; Hexadecimal output
1379 %include "configinit.inc" ; Initialize configuration
1380 %include "parseconfig.inc" ; High-level config file handling
1381 %include "parsecmd.inc" ; Low-level config file handling
1382 %include "bcopy32.inc" ; 32-bit bcopy
1383 %include "loadhigh.inc" ; Load a file into high memory
1384 %include "font.inc" ; VGA font stuff
1385 %include "graphics.inc" ; VGA graphics
1386 %include "highmem.inc" ; High memory sizing
1387 %include "strcpy.inc" ; strcpy()
1388 %include "cache.inc" ; Metadata disk cache
1389 %include "idle.inc" ; Idle handling
1390 %include "adv.inc" ; Auxillary Data Vector
1391 %include "localboot.inc" ; Disk-based local boot
1393 ; -----------------------------------------------------------------------------
1394 ; Begin data section
1395 ; -----------------------------------------------------------------------------
1398 copyright_str db ' Copyright (C) 1994-'
1400 db ' H. Peter Anvin et al', CR, LF, 0
1401 err_bootfailed db CR, LF, 'Boot failed: please change disks and press '
1402 db 'a key to continue.', CR, LF, 0
1403 syslinux_cfg1 db '/boot' ; /boot/syslinux/syslinux.cfg
1404 syslinux_cfg2 db '/syslinux' ; /syslinux/syslinux.cfg
1405 syslinux_cfg3 db '/' ; /syslinux.cfg
1406 config_name db 'syslinux.cfg', 0 ; syslinux.cfg
1409 ; Config file keyword table
1411 %include "keywords.inc"
1414 ; Extensions to search for (in *forward* order).
1416 exten_table: db '.cbt' ; COMBOOT (specific)
1417 db '.bss' ; Boot Sector (add superblock)
1418 db '.bs', 0 ; Boot Sector
1419 db '.com' ; COMBOOT (same as DOS)
1422 dd 0, 0 ; Need 8 null bytes here
1425 ; Misc initialized (data) variables
1427 %ifdef debug ; This code for debugging only
1428 debug_magic dw 0D00Dh ; Debug code sentinel
1432 BufSafe dw trackbufsize/SECTOR_SIZE ; Clusters we can load into trackbuf
1433 BufSafeBytes dw trackbufsize ; = how many bytes?
1435 %if ( trackbufsize % SECTOR_SIZE ) != 0
1436 %error trackbufsize must be a multiple of SECTOR_SIZE