📄 ldlinux.asm
字号:
call getlinsec mov si,bs_magic mov di,ldlinux_magic mov cx,magic_len repe cmpsb ; Make sure that the bootsector jne kaboom ; matches LDLINUX.SYS;; Done! Jump to the entry point!; jmp ldlinux_ent;;; writestr: write a null-terminated string to the console; This assumes we're on page 0. This is only used for early; messages, so it should be OK.;writestr:.loop: lodsb and al,al jz .return mov ah,0Eh ; Write to screen as TTY mov bx,0007h ; Attribute int 10h jmp short .loop.return: ret;; disk_error: decrement the retry count and bail if zero.; This gets patched once we have more space to try to; optimize transfer sizes on broken machines.;disk_error: dec si ; SI holds the disk retry counter jz kaboom ; End of patched "call" instruction! jmp short disk_try_again;; getonesec: like getlinsec, but pre-sets the count to 1;getonesec: mov bp,1 ; Fall through to getlinsec;; getlinsec: load a sequence of BP floppy sector given by the linear sector; number in EAX into the buffer at ES:BX. We try to optimize; by loading up to a whole track at a time, but the user; is responsible for not crossing a 64K boundary.; (Yes, BP is weird for a count, but it was available...);; On return, BX points to the first byte after the transferred; block.;; The "stupid patch area" gets replaced by the code; mov bp,1 ; nop ... (BD 01 00 90 90...) when installing with; the -s option.;; This routine assumes CS == DS.;; Stylistic note: use "xchg" instead of "mov" when the source is a register; that is dead from that point; this saves space. However, please keep; the order to dst,src to keep things sane.;getlinsec: mov esi,[bxSecPerTrack] ; ; Dividing by sectors to get (track,sector): we may have ; up to 2^18 tracks, so we need to use 32-bit arithmetric. ; xor edx,edx ; Zero-extend LBA to 64 bits div esi xor cx,cx xchg cx,dx ; CX <- sector index (0-based) ; EDX <- 0 ; eax = track # div dword [bxHeads] ; Convert track to head/cyl ; ; Now we have AX = cyl, DX = head, CX = sector (0-based), ; BP = sectors to transfer, SI = bsSecPerTrack, ; ES:BX = data target ;gls_nextchunk: push si ; <A> bsSecPerTrack push bp ; <B> Sectors to transfer ; Important - this gets patched with a call. The call ; assumes cx, si and bp are set up, and can modify bp ; and destroy si. Until we have the space to do so, ; transfer one sector at a time.gls_set_size:__BEGIN_STUPID_PATCH_AREA: mov bp,1 ; 3 bytes, same as a call insn__END_STUPID_PATCH_AREA: push ax ; <C> Cylinder # push dx ; <D> Head # push cx ; <E> Sector # shl ah,6 ; Because IBM was STOOPID ; and thought 8 bits were enough ; then thought 10 bits were enough... pop cx ; <E> Sector # push cx ; <E> Sector # inc cx ; Sector numbers are 1-based, sigh or cl,ah mov ch,al mov dh,dl mov dl,[bsDriveNumber] xchg ax,bp ; Sector to transfer count ; (xchg shorter than mov) mov si,retry_count ; # of times to retry a disk access;; Do the disk transfer... save the registers in case we fail :(;disk_try_again: pusha ; <F> mov ah,02h ; READ DISK int 13h popa ; <F> jc disk_error;; Disk access successful; pop cx ; <E> Sector # mov di,ax ; Reduce sector left count mul word [bsBytesPerSec] ; Figure out how much to advance ptr add bx,ax ; Update buffer location pop dx ; <D> Head # pop ax ; <C> Cyl # pop bp ; <B> Sectors left to transfer pop si ; <A> Number of sectors/track sub bp,di ; Reduce with # of sectors just read jz writestr.return ; Done! add cx,di cmp cx,si jb gls_nextchunk inc dx ; Next track on cyl cmp dx,[bsHeads] ; Was this the last one? jb gls_nonewcyl inc ax ; If so, new cylinder xor dx,dx ; First head on new cylindergls_nonewcyl: sub cx,si ; First sector on new track jmp short gls_nextchunkbailmsg: db 'Boot failed', 0Dh, 0Ah, 0bs_checkpt equ $ ; Must be <= 7DEFh%if 1bs_checkpt_off equ ($-$$)%ifndef DEPEND%if bs_checkpt_off > 1EFh%error "Boot sector overflow"%endif%endif zb 1EFh-($-$$)%endifbs_magic equ $ ; From here to the magic_len equ ; must match ldlinux_magicldlinux_name: db 'LDLINUX SYS' ; Looks like this in the root dir dd HEXDATE ; Hopefully unique between compilesbootsignature dw 0AA55hmagic_len equ $-bs_magic;; ===========================================================================; End of boot sector; ===========================================================================; Start of LDLINUX.SYS; ===========================================================================ldlinux_sys:syslinux_banner db 0Dh, 0Ah%if IS_MDSLINUX db 'MDSLINUX '%else db 'SYSLINUX '%endif db version_str, ' ', date, ' ', 0 db 0Dh, 0Ah, 1Ah ; EOF if we "type" this in DOSldlinux_magic db 'LDLINUX SYS' dd HEXDATE dw 0AA55h;; This area is possibly patched by the installer. It is located; immediately after the EOF + LDLINUX SYS + 4 bytes + 55 AA + alignment,; so we can find it algorithmically.; alignb 4, db 0MaxTransfer dw 00FFh ; Absolutely maximum transfer size align 4ldlinux_ent:; ; Note that some BIOSes are buggy and run the boot sector at 07C0:0000; instead of 0000:7C00 and the like. We don't want to add anything; more to the boot sector, so it is written to not assume a fixed; value in CS, but we don't want to deal with that anymore from now; on.; jmp 0:.next.next:;; Tell the user we got this far; mov si,syslinux_banner call writestr;; Remember, the boot sector loaded only the first cluster of LDLINUX.SYS.; We can really only rely on a single sector having been loaded. Hence; we should load the FAT into RAM and start chasing pointers...; xor ax,ax cwd inc dx ; DX:AX <- 64K div word [bxBytesPerSec] ; sectors/64K mov si,ax push es mov bx,fat_seg ; Load into fat_seg:0000 mov es,bx mov eax,[bsHidden] ; Hidden sectors add edx,[bxResSectors] add eax,edx mov ecx,[bxFATsecs] ; Sectors/FATfat_load_loop: mov ebp,ecx ; Make sure high EBP = 0 cmp bp,si jna fat_load mov bp,si ; A full 64K mobyfat_load: xor bx,bx ; Offset 0 in the current ES call getlinsecsr sub cx,bp jz fat_load_done ; Last moby? add eax,ebp ; Advance sector count mov bx,es ; Next 64K moby add bx,1000h mov es,bx jmp short fat_load_loopfat_load_done: pop es;; Fine, now we have the FAT in memory. How big is a cluster, really?; Also figure out how many clusters will fit in an 8K buffer, and how; many sectors and bytes that is; mov edi,[bxBytesPerSec] ; Used a lot below mov eax,[SecPerClust] mov si,ax ; Also used a lot mul di mov [ClustSize],eax ; Bytes/cluster mov bx,ax mov ax,trackbufsize ; High bit 0 cwd div bx mov [BufSafe],ax ; # of cluster in trackbuf mul si mov [BufSafeSec],ax mul di mov [BufSafeBytes],ax add ax,getcbuf ; Size of getcbuf is the same mov [EndOfGetCBuf],ax ; as for trackbuf;; FAT12 or FAT16? This computation is fscking ridiculous...; mov eax,[bxSectors] and ax,ax jnz have_secs mov eax,[bsHugeSectors]have_secs: add eax,[bsHidden] ; These are not included sub eax,[RootDir] ; Start of root directory movzx ebx,word [RootDirSize] sub eax,ebx ; Subtract root directory size xor edx,edx div esi ; Convert to clusters cmp ax,4086 ; FAT12 limit jna is_fat12 ; Patch the jump mov byte [nextcluster+1],nextcluster_fat16-(nextcluster+2)is_fat12:;; Patch gls_set_size so we can transfer more than one sector at a time.; mov byte [gls_set_size],0xe8 ; E8 = CALL NEAR mov word [gls_set_size+1],do_gls_set_size-(gls_set_size+3) mov byte [disk_error],0xe8 mov word [disk_error+1],do_disk_error-(disk_error+3);; Now we read the rest of LDLINUX.SYS. Don't bother loading the first; cluster again, though.;load_rest: mov cx,[ClustSize] mov bx,ldlinux_sys add bx,cx mov si,[RunLinClust] call nextcluster xor dx,dx mov ax,ldlinux_len-1 ; To be on the safe side add ax,cx div cx ; the number of clusters dec ax ; We've already read one jz all_read_jmp mov cx,ax call getfssec;; All loaded up;all_read_jmp: jmp all_read;; -----------------------------------------------------------------------------; Subroutines that have to be in the first sector; -----------------------------------------------------------------------------;; getfssec: Get multiple clusters from a file, given the starting cluster.;; This routine makes sure the subtransfers do not cross a 64K boundary,; and will correct the situation if it does, UNLESS *sectors* cross; 64K boundaries.;; ES:BX -> Buffer; SI -> Starting cluster number (2-based); CX -> Cluster count (0FFFFh = until end of file);; Returns CF=1 on EOF;getfssec:.getfragment: xor ebp,ebp ; Fragment sector count lea eax,[si-2] ; Get 0-based sector address mul dword [SecPerClust] add eax,[DataArea].getseccnt: ; See if we can read > 1 clust add bp,[SecPerClust] dec cx ; Reduce clusters left to find lea di,[si+1] call nextcluster cmc jc .eof ; At EOF? jcxz .endfragment ; Or was it the last we wanted? cmp si,di ; Is file continuous? je .getseccnt ; Yes, we can get.endfragment: clc ; Not at EOF.eof: pushf ; Remember EOF or not push si push cx.getchunk: push eax mov ax,es ; Check for 64K boundaries. shl ax,4 add ax,bx xor dx,dx neg ax setz dl ; DX <- 1 if full 64K segment div word [bsBytesPerSec] ; How many sectors fit? mov si,bp sub si,ax ; Compute remaining sectors jbe .lastchunk mov bp,ax pop eax call getlinsecsr add eax,ebp ; EBP<31:16> == 0 mov bp,si ; Remaining sector count jmp short .getchunk.lastchunk: pop eax call getlinsec pop cx pop si popf jcxz .return ; If we hit the count limit jnc .getfragment ; If we didn't hit EOF.return: ret;; getlinsecsr: save registers, call getlinsec, restore registers;getlinsecsr: pushad call getlinsec popad ret;; nextcluster: Advance a cluster pointer in SI to the next cluster; pointed at in the FAT tables. CF=0 on return if end of file.;nextcluster: jmp short nextcluster_fat12 ; This gets patchednextcluster_fat12: push bx push ds mov bx,fat_seg mov ds,bx mov bx,si ; Multiply by 3/2 shr bx,1 ; CF now set if odd mov si,[si+bx] jnc nc_even shr si,4 ; Needed for odd onlync_even: and si,0FFFh cmp si,0FF0h ; Clears CF if at end of file pop ds pop bxnc_return: ret;; FAT16 decoding routine. Note that a 16-bit FAT can be up to 128K,; so we have to decide if we're in the "low" or the "high" 64K-segment...;nextcluster_fat16: push ax push ds mov ax,fat_seg shl si,1 jnc .seg0 mov ax,fat_seg+1000h.seg0: mov ds,ax mov si,[si] cmp si,0FFF0h pop ds pop ax ret;; Routine that controls how much we can transfer in one chunk. Called; from gls_set_size in getlinsec.;do_gls_set_size: sub si,cx ; Sectors left on track cmp bp,si jna .lastchunk mov bp,si ; No more than a trackful, please!.lastchunk: cmp bp,[MaxTransfer] ; Absolute maximum transfer size jna .oktransfer mov bp,[MaxTransfer].oktransfer: ret;; This routine captures disk errors, and tries to decide if it is; time to reduce the transfer size.;do_disk_error: dec si ; Decrement the retry counter jz kaboom ; If expired, croak cmp si,2 ; If only 2 attempts left ja .nodanger mov al,1 ; Drop transfer size to 1 jmp short .setsize.nodanger: cmp si,retry_count-2 ja .again ; First time, just try again shr al,1 ; Otherwise, try to reduce adc al,0 ; the max transfer size, but not to 0.setsize: mov [MaxTransfer],al.again: ret;; Debug routine;%ifdef debugsafedumpregs: cmp word [Debug_Magic],0D00Dh jnz nc_return jmp dumpregs%endifrl_checkpt equ $ ; Must be <= 8000hrl_checkpt_off equ ($-$$)%ifndef DEPEND%if rl_checkpt_off > 400h%error "Sector 1 overflow"%endif%endif; ----------------------------------------------------------------------------; End of code and data that have to be in the first sector; ----------------------------------------------------------------------------all_read:;; Let the user (and programmer!) know we got this far. This used to be; in Sector 1, but makes a lot more sense here.;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -