; second.S - LILO second stage boot loader ; ; Copyright 1992-1998 Werner Almesberger ; Copyright 1999-2006 John Coffman ; Copyright 2009-2011 Joachim Wiedorn ; All rights reserved. ; ; Licensed under the terms contained in the file 'COPYING' ; in the source directory. ; /*#define DEBUG*/ #define REG_DUMP 1 /*#define DELL_DIRTY_HACK*/ #define PIXADDRESS #define MEMORY_CHECK #define RETAIN #define DNAME 1 #define LILO_ASM #include "lilo.h" get common.s /* as86 "include" will bypass the CPP */ #define MAP Map #define MAP2 Map2 #define DFLCMD Dflcmd #define DESCR Descr #define KEYTABLE Keytable #define PARMLINE Parmline #define DEBUG_INITRD 0 #ifdef DEBUG #define DEBUG_NEW 1 #else #if VERSION_MINOR<50 #define DEBUG_NEW 0 #else #define DEBUG_NEW 1 #ifndef MEMORY_CHECK #define MEMORY_CHECK #endif #endif /* VERSION_MINOR */ #endif /* DEBUG */ /* The following is set to 1 to enable map file writing */ #if DEBUG_NEW # define WR_ENABLE 2 # if VERSION_MINOR>=90 # undef WR_ENABLE # define WR_ENABLE 1 # endif #else # define WR_ENABLE 1 #endif #if DEBUG_NEW==0 && !(WR_ENABLE&1) #error "Retail version should have WR_ENABLE=1" #endif /* if compiling READONLY, then WR_ENABLE should never be used */ #ifdef LCF_READONLY # undef WR_ENABLE #endif #if ! NO_FS #if DEBUG_NEW #define CHECK_FS_VERBOSE 0 #define BEG_FS call fs_check #else #define BEG_FS #endif #define SEG_FS seg fs #define END_FS #else #define BEG_FS db 0x1e,0x2e,0x8e,0x1e,0x02,0x00 /* push ds; \ seg cs; \ mov ds,firstseg */ #define SEG_FS #define END_FS pop ds #endif /* get rid of the following to revert to old int 0x15/fn 0x88 mem scheme */ #define HIGHMEM_MAX 0x38000000 LOADSEG = SYSSEG ; max kernel = 1024 sectors #define UI_MAGIC 0xff /* take input from keyboard */ #ifdef MENU STAGE_MENU = STAGE_FLAG_MENU X=MENU #else STAGE_MENU = 0 #endif #ifdef BITMAP STAGE_BITMAP = STAGE_FLAG_BMP4 X=BITMAP #else STAGE_BITMAP = 0 #endif #ifdef LCF_NOSERIAL STAGE_SERIAL = 0 #else STAGE_SERIAL = STAGE_FLAG_SERIAL #endif #ifdef TEXT X=TEXT #endif .text .globl _main .org 0 _main: jmp start #if NO_FS || DEBUG_NEW firstseg: dw 0 # if DEBUG_NEW # define CHECK_FS call fs_check # else # define CHECK_FS # endif #endif #ifndef CHECK_FS # define CHECK_FS #endif .org 6 ! Boot device parameters. They are set by the installer. sig: .ascii "LILO" version: .word VERSION mapstamp: .long 0 stage: .word STAGE_SECOND|STAGE_SERIAL|STAGE_MENU|STAGE_BITMAP port: .byte 0 ; COM port (0 = unused, 1 = COM1, etc.) sparam: .byte 0 ; serial port parameters (0 = unused) timout: .word 0 ; input timeout delay: .word 0 ; boot delay ms_len: .word 0 ; initial greeting message kt_cx: .word 0 ; keyboard translation table kt_dx: .word 0 kt_al: .byte 0 flag2: .byte 0 ; second stage specific flags ! GDT for "high" loading .align 16 gdt: ; space for BIOS .blkb 0x10 ; source .word 0xffff ; no limits .byte 0 .word LOADSEG>>4 ; start: 0x10000 .byte 0x93 ; permissions .word 0 ; padding for 80286 mode :-( ; destination .word 0xffff ; no limits .word 0 ; start - filled in by user .byte 0 .byte 0x93 ; permissions .word 0 ; padding for 80286 mode :-( ; space for BIOS .blkb 0x10 start: cld ; only CLD in the code; there is no STD #if ! NO_FS push ds pop fs ; address parameters from here #endif #if NO_FS || DEBUG_NEW seg cs mov firstseg,ds ; save DS here #endif seg cs mov [init_dx],dx ; save DX passed in from first.S int 0x12 ; get memory available CHECK_FS #if EBDA_EXTRA sub ax,#EBDA_EXTRA ; allocate extra EBDA #endif shl ax,#6 ; convert to paragraphs sub ax,#Dataend/16 mov es,ax ; destination address push cs pop ds xor si,si xor di,di xor ax,ax mov cx,#max_secondary/2 ; count of words to move rep movsw add di,#BSSstart-max_secondary mov cx,#BSSsize/2 rep stosw push es push #continue retf ; branch to continue address continue: #ifdef DELL_DIRTY_HACK ;;; push dx ; preserve DX (already saved) mov ax,#0x1200 ; enable video (VGA) mov bl,#0x36 ; (probably a nop on EGA or MDA) int 0x10 ; video call CHECK_FS ;;; pop dx ; restore DX on Dell geforce nVidia card #endif #ifndef LCF_NOSERIAL call serial_setup ; set up the COM port, if any #endif #ifndef LCF_NODRAIN mov cx,#32 ; drain type-ahead buffer ? drkbd: mov ah,#1 ; is a key pressed ? int 0x16 jz comcom ; no -> done xor ah,ah ; get the key int 0x16 loop drkbd #endif comcom: CHECK_FS mov al,#0x4c ; display an 'L' call display push #0 ; get pointer to disk parameter table in DS:SI pop ds lds si,[0x78] ; 0x78 = 4*0x1E #ifndef LCF_XL_SECS cmp byte ptr (si+4),#9 ; okay ? ja dskok ; yes -> do not patch #endif push cs ; get pointer to new area in ES:DI pop es mov di,#dskprm mov cx,#6 ; copy 12 bytes rep movsw seg es ; patch number of sectors #ifndef LCF_XL_SECS mov byte ptr (di-8),#18 #else mov byte ptr (di-8),#LCF_XL_SECS #endif push #0 pop ds cli ; paranoia mov [0x78],#dskprm mov [0x7a],es sti dskok: #ifndef LCF_NOSERIAL seg cs ; clear the break flag mov byte ptr break,#0 #endif call instto ; get timer interrupt CHECK_FS ;;; jmp restrt ; get going ! Restart here after a boot error restrt: mov bx,cs ; adjust segment registers mov ds,bx mov es,bx sub bx,#MAX_SETUPSECS*0x20+0x20 ; segment for setup code & ; bootsect mov cx,#INITSEG cmp bx,cx jbe restrt1 mov bx,cx ; BX is the smaller segment # restrt1: mov word ptr [map],#MAP mov [initseg],bx ; set up INITSEG (was 0x9000) lea cx,(bx+0x20) mov [setupseg],cx ; set up SETUPSEG (was 0x9020) mov cx,cs sub cx,bx ; subtract [initseg] shl cx,#4 ; get stack size mov ss,bx ; must lock with move to SP below mov sp,cx ; data on the stack) #if DEBUG_NEW pusha mov bx,#msg_where call say mov ax,[initseg] call wout mov ax,[setupseg] call swout mov ax,cs call swout mov ax,ss call swout mov al,#0x3A ; colon call display mov ax,sp call wout mov al,#32 ; space call display BEG_FS SEG_FS ; external parameters ? mov ax,[EX_OFF+6] ; DH:DL as passed to first.S END_FS call swout mov ax,[init_dx] ; DX into second Stage call swout #ifdef LCF_NOKEYBOARD call nkbdbg0 .ascii " flags2=" .byte 0 nkbdbg0: pop bx call say mov al,[par2_flag2] call bout #endif call crlf #if REG_DUMP call frd0 .ascii "Registers at startup of first stage loader:\n" .ascii " AX BX CX DX SI DI BP DS ES\n" .byte 0 frd0: pop bx call say BEG_FS SEG_FS ; external parameters ? mov ax,[EX_OFF-2-4] ; AX call wout SEG_FS ; external parameters ? mov ax,[EX_OFF-8-4] ; BX call swout SEG_FS ; external parameters ? mov ax,[EX_OFF-4-4] ; CX call swout SEG_FS ; external parameters ? mov ax,[EX_OFF-6-4] ; DX call swout SEG_FS ; external parameters ? mov ax,[EX_OFF-14-4] ; SI call swout SEG_FS ; external parameters ? mov ax,[EX_OFF-16-4] ; DI call swout SEG_FS ; external parameters ? mov ax,[EX_OFF-12-4] ; BP call swout SEG_FS ; external parameters ? mov ax,[EX_OFF-18+16] ; DS call swout SEG_FS ; external parameters ? mov ax,[EX_OFF-20+16] ; ES call swout END_FS call crlf #endif popa #endif cmp dword [sig],#EX_MAG_HL ; "LILO" jne crshbrn2 cmp dword [mcmdbeg+6],#0x4547414d ; "MAGE" from BOOT_IMAGE jne crshbrn2 cmp BYTE [stage],#STAGE_SECOND #if 1 jne crshbrn cmp WORD [version],#VERSION #endif crshbrn2: jne crshbrn mov [cmdbeg],#acmdbeg ; probably unattended boot mov di,#devmap ; place to store the device map #ifdef LCF_FIRST6 mov ah,[init_dx] ; AH is physical device BEG_FS SEG_FS mov al,[par1_secondary+0+SSDIFF] ; map device logical END_FS #else mov ax,[init_dx] ; AH is flags & device, AL is physical device xchg ah,al and ax,#DEV_MASK_asm<<8 | DEV_MASK_asm ; mask to pure device codes #endif cmp ah,al je end_tt #if DEBUG_NEW pusha call wout ; TT entry, maybe call crlf popa #endif stosw ; set up the translation from map -> boot end_tt: xor ax,ax stosw ldsc: BEG_FS SEG_FS mov eax,[par1_mapstamp] END_FS cmp eax,[par2_mapstamp] jne timeerr call kt_read ; read the KEYTABLE call build_vol_tab mov bx,#DESCR mov si,#KEYTABLE+256+mt_descr descr_more: lodsw xchg cx,ax lodsw xchg dx,ax lodsb call cread jc near fdnok ; error -> retry add bh,#2 ; increment address cmp si,#KEYTABLE+256+mt_descr+sa_size*MAX_DESCR_SECS_asm jb descr_more mov si,#DESCR ; compute a checksum of the descriptor table mov di,#SECTOR_SIZE*MAX_DESCR_SECS-4 push dword #CRC_POLY1 call crc32 add di,si cmp eax,dword (di) jz nochkerr ! Timestamp error timeerr: mov bx,#msg_time jmp zz ! Checksum error chkerr: mov bx,#msg_chkerr jmp zz ; go wait crshbrn: mov bx,#msg_sigerr ; signature not found zz: call say zzz: hlt ; wait for interrupt jmp zzz ; sit here forever nochkerr: #ifdef DEBUG pusha mov bx,#nochker_msg call say popa jmp nochkerr1 nochker_msg: .ascii "Descriptor checksum okay\n" .byte 0 nochkerr1: #endif #ifdef LCF_VIRTUAL ; remove those items that have "vmdisable", if virtual boot call vmtest jnc virtual_done mov di,#DESCR0 ; point at first descriptor vir_loop: test byte ptr [id_name](di),#0xFF ; test for NUL name jz virtual_done test word ptr [id_flags](di),#FLAG_VMDISABLE jz vir_skip push di lea si,[id_size](di) vir_loop1: mov cx,#id_size rep movsb test byte ptr [id_name](di),#0xFF jnz vir_loop1 pop di jmp vir_loop vir_skip: add di,#id_size jmp vir_loop virtual_done: #endif #ifdef LCF_NOKEYBOARD ; remove those items that have "nokbdisable", if nokeyboard boot call kbtest jc kbd_done mov di,#DESCR0 ; point at first descriptor kbd_loop: test byte ptr [id_name](di),#0xFF ; test for NUL name jz kbd_done test word ptr [id_flags](di),#FLAG_NOKBDISABLE jz kbd_skip push di lea si,[id_size](di) kbd_loop1: mov cx,#id_size rep movsb test byte ptr [id_name](di),#0xFF jnz kbd_loop1 pop di jmp kbd_loop kbd_skip: add di,#id_size jmp kbd_loop kbd_done: #endif #if defined(MENU) || defined(BITMAP) xor bx,bx ; defaults are all zero mov [dimage],bx ; set default image to boot mov [abs_cx],bx ; upper left of scroll area ; means screen is not cleared #endif mov bx,#KEYTABLE+256 mov al,(bx+mt_flag) BEG_FS SEG_FS ; get possible FLAG_NOBD or byte ptr [par1_prompt+SSDIFF],al END_FS #ifdef MENU call title_stuff #endif mov bx,#DFLCMD ;BEG_FS ;SEG_FS mov cx,mt_dflcmd+KEYTABLE+256 ;DFCMD_OFF ;SEG_FS mov dx,mt_dflcmd+2+KEYTABLE+256 ;SEG_FS mov al,mt_dflcmd+4+KEYTABLE+256 ;END_FS call cread jc fdnok ; error -> retry mov bx,#DFLCMD cmp word ptr (bx),#DC_MAGIC ; okay ? jne bdcmag ; no -> do not write #ifndef LCF_READONLY mov word ptr (bx),#DC_MGOFF ; erase the magic number call cmd_write ; write out the command line #if 0 ; 22.6.2 -- removed, because this is worse that the first ; command lock bug mov si,#DESCR0 lea di,(bx+2) mov cx,#16 rep movsb ; 22.6.2 #endif #endif jmp dokay ; continue bdcmag: mov byte ptr (bx+2),#0 ; disable the command line jmp dokay ; go on fdnok: #if 0 xor ax,ax ; reset FDC mov dl,al int 0x13 #endif br ldsc ; retry ! List all known boot images list: mov byte ptr (bx),#0 ; set EOL marker call crlf #ifdef MENU inc word [suppress] ; suppress console output #endif mov si,#DESCR0 ; list all images mov cx,#IMAGES xor dl,dl ; DL counts the images lloop: testb (si),#0xff ; done ? jz ldone ; yes mov bx,si ; display the name call say add si,#MAX_IMAGE_NAME+4 inc dl ; count the image test dl,#3 ; inside line -> go on jnz fill call crlf jmp imgdne ; next image fill: push bx ; fill with spaces mov al,#0x20 call display pop bx inc bx cmp bx,si jbe fill imgdne: add si,#id_size-MAX_IMAGE_NAME-4 loop lloop ; next image ldone: test dl,#3 ; already at BOL ? jz atbol ; yes -> no CRLF call crlf atbol: #ifdef MENU dec word [suppress] #endif br iloop ; done ! Ready to process user input dokay: mov bx,#ospc ; display 'O ' call say /* ifdef HIGHMEM_MAX */ xor eax,eax mov dword ptr [hma],eax /* #endif */ mov ospc,al ; disable the message mov word ptr vgaovr,#VGA_NOCOVR ; disable VGA override ;; BEG_FS ;; SEG_FS xchg ax,par2_delay ;DSC_OFF-8+SSDIFF ;; END_FS or old_del,ax ; remember delay mov nodfl,#iloop ; interactive prompt if falling through #ifdef LCF_NOKEYBOARD call kbtest ; keyboard present? #ifndef LCF_NOSERIAL jc kbd_present ; no PC keyboard on the system, is there a serial port in use? cmp byte ptr [par2_port],#0 jz skip_prompt ; no serial keyboard either #else jnc skip_prompt ; skip check for prompt if no keyboard #endif kbd_present: #endif BEG_FS SEG_FS ; enter boot prompt ? test byte ptr par1_prompt+SSDIFF,#FLAG_PROMPT ;DSC_OFF+15+SSDIFF,#0 END_FS jnz extp ; yes -> check for external parameters skip_prompt: mov nodfl,#bfirst ; boot first image if falling through call waitsh ; wait for a shifting key jc iloop ; key pressed -> enter interactive mode ! Check for external parameters extp: BEG_FS SEG_FS ; external parameters ? cmp byte ptr EX_OFF+6,#EX_DL_MAG END_FS jne noex ; no -> go on BEG_FS SEG_FS mov bl,EX_OFF+7 ; get drive SEG_FS ; clear flag mov byte ptr EX_OFF+6,bl ; clear flag SEG_FS ; load the signature pointer les bx,EX_OFF END_FS seg es cmp dword ptr (bx),#EX_MAG_HL ; "LILO" jne noex ; no -> go on BEG_FS SEG_FS mov si,EX_OFF+4 ; pointer to the command line END_FS seg es cmp byte ptr (si),#0 ; empty ? je iloop ; yes -> enter interactive mode jmp niloop ; enter non-interactive mode ! No external parameters after timeout -> boot first image noex: push cs ; restore ES pop es mov si,#DFLCMD+2 ; default command line ? cmp byte ptr (si),#0 jne niloop ; yes -> use it mov ax,nodfl ; no idea how to tell as86 to do jmp (addr) :-( jmp ax ; fall through ; Command input processor iloop: #if defined(MENU) || defined(BITMAP) call menu_setup #endif #ifndef BITMAP ;; BEG_FS ;; SEG_FS ; message disabled ? cmp word ptr par2_msg_len,#0 ;MSG_OFF+SSDIFF,#0 ;; END_FS je nomsg ; yes -> skip this call crlf ;BEG_FS ;SEG_FS ; load the message file mov cx,mt_msg+KEYTABLE+256 ;MSG_OFF+SSDIFF+2 ;SEG_FS mov dx,mt_msg+2+KEYTABLE+256 ;SEG_FS mov al,mt_msg+4+KEYTABLE+256 ;END_FS mov bx,[map] call sread call loadfile xor bx,bx ; set the terminating NUL and disable further ; messages xchg bx,par2_msg_len ;MSG_OFF+SSDIFF push #SYSSEG pop ds mov byte ptr (bx),#0 xor bx,bx ; display the message call say push cs ; restore segment registers pop ds #endif nomsg: push cs ; disable external parameters pop es mov cmdbeg,#acmdbeg ; probably unattended boot mov si,#usrinpm ; interactive mode niloop: ; ES may point to external params mov bx,#msg_p ; display boot prompt call say mov bx,#cmdline ; move cursor to the end of the line clend: mov al,(bx) or al,al ; at end ? jz cledne ; yes -> go on push bx ; display the character call display pop bx inc bx ; next one jne clend cledne: mov byte ptr prechr,#32 ; character before command line is a space ! Input loop input: seg es ; interactive mode ? cmp byte ptr (si),#UI_MAGIC je kbinp ; yes -> get keyboard input seg es ; get non-interactive input mov al,(si) inc si jmp gotinp ; go on tolist: #ifdef BITMAP call menu_exit #endif br list ; ... kbinp: mov cx,#brto ; get a key call getkey #ifdef BITMAP cmp byte [abs_cx+1],#0 je noNull ; skip cursor keys after Tab #endif #if defined(MENU) || defined(BITMAP) cmp al,#0xE0 ; extended keyboard je toNull or al,al ; toNull: je near null ; cursor control #endif #ifndef MENU noNull: or al,al ; keyboard NUL input? je input ; yes, skip Keyboard NUL ; stored command line NUL is handled differently #endif gotinp: cmp al,#9 ; TAB ? je tolist ; yes -> list images cmp al,#63 ; "?" ? je tolist ; yes -> list images or al,al ; NUL ? je nul ; yes -> go on cmp al,#8 ; BS ? je todelch ; yes -> erase one character cmp al,#13 ; CR ? je cr ; yes -> go on cmp al,#127 ; DEL ? je todelch ; yes -> erase one character ja input ; non-printable -> ignore it cmp al,#21 ; ^U ? je todell ; yes -> erase the line cmp al,#24 ; ^X ? je todell ; yes -> erase the line cmp al,#32 ; ignore non-printable characters except space jb input ja noblnk ; no space -> go on cmp (bx-1),al ; second space in a row ? je input ; yes -> ignore it noblnk: cmp bx,#cmdline+CL_LENGTH-1 ; at end of buffer ? je input ; yes -> ignore xor ah,ah ; cmdline is always NUL terminated mov (bx),ax ; store in the buffer inc bx ; increment pointer push bx call display ; echo #if defined(MENU) || defined(BITMAP) push ax call find_image ; we want the side effect of the hilite pop ax #endif pop bx cmp bx,#cmdline+1 ; first character ? jne input ; no -> next input #ifdef LCF_IGNORECASE call upcase ; convert to upper case #endif mov cx,#IMAGES ; check if we have a single-key entry mov di,#DESCR0 mov ah,al sklp: test word ptr (di+id_flags),#FLAG_SINGLE ; single-key entry ? jz sknext ; no -> try next mov al,(di) ; get first character #ifdef LCF_IGNORECASE call upcase ; convert to upper case #endif cmp al,ah ; do we have a match ? jne sknext ; no -> try next cmp byte ptr (di+1),#0 ; at end ? je cr ; yes -> run it sknext: add di,#id_size ; test next entry loop sklp ; next one br input ; done -> get more input todelch:br delch ; ... todell: br delline ; ... ! End of input, process the command line nul: push bx ; automatic boot - wait for timeout mov ax,old_del call waitsh pop bx jnc crnul ; no key pressed -> continue mov bx,#msg_int ; interrupted -> display a message call say mov byte ptr cmdline,#0 ; clear the command line br iloop ; return to interactive prompt cr: ;;22.7 mov word par2_timeout,#0xffff ; kill timeout #ifdef LCF_HP_TTRC push ax ; HP TTRC boot fail workaround. mov ax, #3 ; 2000/10 call setto ; reload timer short_wait: test byte ptr timeout,#1 ; timed out ? jz short_wait ; No, remain loop.. pop ax #endif mov cmdbeg,#mcmdbeg ; probably manual boot crnul: #ifndef LCF_NOSERIAL mov byte ptr break,#0 ; clear the break flag #endif push cs ; set ES to CS pop es xor al,al ; mark end mov (bx),al mov si,#cmdline ; copy command line to save buffer mov di,#lkcbuf mov byte ptr dolock,#0 ; disable locking cpsav: lodsb ; copy one byte stosb or al,al ; at end ? jnz cpsav ; no -> go on cmp bx,#cmdline ; empty line ? je notrspc ; yes -> boot first image cmp byte ptr (bx-1),#32 ; trailing space ? jne notrspc ; no -> go on dec bx ; remove the space mov byte ptr (bx),al notrspc:mov si,#cmdline ; scan the command line for "vga=", "kbd=", mov di,si ; "lock" or "mem=" chkvga: #ifdef LCF_BDATA vsktnbd: cmp dword ptr (si),#0x64626f6e ; "nobd" jne vsktv cmp byte (si+4),#32 ; terminated with SP or NUL? jnbe vsktv BEG_FS SEG_FS ; enter boot prompt ? or byte ptr par1_prompt+SSDIFF,#FLAG_NOBD ; suppress BIOS data collection END_FS jmp vskwd ; skip word #endif vsktv: cmp dword ptr (si),#0x3d616776 ; "vga=" jne vsktk call setvga ; set VGA mode jc near iloop ; error -> get next command jmp vskdb ; proceed by discarding last blank vsktk: cmp dword ptr (si),#0x3d64626b ; "kbd=" jne vsktl call putkbd ; pre-load keyboard buffer jmp vskdb ; proceed by discarding last blank vsktl: cmp dword ptr (si),#0x6b636f6c ; "lock" jne vsktm cmp byte (si+4),#32 ; space? jnbe vsktm mov byte ptr dolock,#1 ; enable locking vskwd: add si,#4 ; skip word vskdb: dec di ; discard last blank jmp vsknb ; continue vsktm: #if DEBUG_INITRD cmp dword ptr (si),#0x3d647269 ; "ird=" #else cmp dword ptr (si),#0x3d6d656d ; "mem=" #endif jne vsknb call getmem ; get the user-provided memory limit vsknb: lodsb ; copy one byte stosb cmp al,#32 ; space ? je chkvga ; yes -> look for options again or al,al ; at end ? jnz vsknb ; no -> go on call crlf ; write CR/LF cmp di,#cmdline+1 ; empty line ? emptyl: je bfirst ; yes -> boot first image jmp bcmd ; boot the specified image ! Find the boot image and start it bcmd: call find_image jc near boot ; eureka, it was found mov bx,#msg_nf ; not found -> display a message call say br iloop ; get more input ! Delete one character delch: cmp bx,#cmdline ; at the beginning ? je toinput ; yes -> do nothing dec bx ; move the pointer push bx ; display[B BS,SPC,BS mov bx,#bs call say #if defined(MENU) || defined(BITMAP) pop bx push bx mov byte (bx),#0 ; NUL terminate the line mov ax,#cmdline sub ax,bx jz delch6 call find_image jmp delch9 delch6: #if defined(LCF_VIRTUAL) || defined(LCF_NOKEYBOARD) mov ax,[vimage] #endif mov bx,[dimage] cmp ax,bx je delch9 xchg ax,bx call lowlite xchg ax,bx call hilite delch9: #endif pop bx toinput:br input ; go on ! Delete the entire line delline: #if !defined(MENU) && !defined(BITMAP) cmp bx,#cmdline ; done ? je toinput ; yes -> go on push bx ; display BS,SPC,BS mov bx,#bs call say pop bx dec bx ; move the pointer jmp delline ; next one #else call menu_delline push bx ; delch will do a pop xor ax,ax jmp delch6 #endif ! Boot first after timeout brto: call crlf ; display a CRLF jmp brfrst ; boot ! Boot the first image bfirst: mov byte ptr lkcbuf,#0 ; clear default cmp byte ptr cmdline,#0 ; is there a default ? jne bcmd ; yes -> boot that image brfrst: mov bx,#DESCR0 ; boot the first image #if defined(LCF_VIRTUAL) && defined(LCF_NOKEYBOARD) xor ax,ax ; mask = 0 call vmtest jnc brfrst0v ; not virtual mov ax,#FLAG_VMDEFAULT brfrst0v: call kbtest jc brfrst0k mov ax,#FLAG_NOKBDEFAULT brfrst0k: mov cx,#IMAGES brfrst1: test word ptr (bx+id_flags),ax jnz brfrst3 add bx,#id_size loop brfrst1 mov bx,#DESCR0 ; restore default brfrst3: #else #ifdef LCF_VIRTUAL call vmtest jnc brfrst3 ; not virtual, boot BX mov cx,#IMAGES brfrst1: test word ptr (bx+id_flags),#FLAG_VMDEFAULT jnz brfrst3 add bx,#id_size loop brfrst1 mov bx,#DESCR0 ; restore default brfrst3: #endif /* LCF_VIRTUAL */ #ifdef LCF_NOKEYBOARD call kbtest jc brfrst3k ; not virtual, boot BX mov cx,#IMAGES brfrst1k: test word ptr (bx+id_flags),#FLAG_NOKBDEFAULT jnz brfrst3k add bx,#id_size loop brfrst1k mov bx,#DESCR0 ; restore default brfrst3k: #endif /* LCF_NOKEYBOARD */ #endif /* if !both */ mov si,bx ; copy the name to the command line mov di,#cmdline bfcpl: lodsb ; copy one character mov (di),al inc di or al,al ; NUL ? jnz bfcpl ; no -> next one ! Boot the image BX points to (with password check) boot: mov word par2_timeout,#0xffff ; kill timeout (22.7) mov si,#cmdline ; locate start of options locopt: lodsb or al,al ; NUL ? je optfnd ; yes -> no options cmp al,#32 ; space ? jne locopt ; no -> continue searching cmp byte ptr (si),#0 ; followed by NUL ? jne optfnd ; no -> go on mov byte ptr (si-1),#0 ; discard trailing space optfnd: dec si ; adjust pointer mov options,si ; store pointer for later use #ifdef BITMAP #ifdef RETAIN test word ptr [id_flags](bx),#FLAG_RETAIN ; keep bitmap? jz bmp_terminate xor ax,ax ; time out immediately call waitsh ; check for break (Shift, Alt, ScrollLock,...) jnc bmp_retain bmp_terminate: #endif call menu_exit bmp_retain: #endif #ifdef LCF_VIRTUAL test word ptr [id_flags](bx),#FLAG_VMWARN jz boot9 call vmtest ; 'vmwarn' there, is it actually virt. boot jnc boot9 ; VMWARN set, and is virtual boot, so issue comment ;; BEG_FS ;; SEG_FS mov word ptr par2_timeout,#0xffff ; cancel timeout ;; END_FS push bx ; save image descriptor ptr mov bx,#msg_vmwarn call say mov cx,#vmwto ; timeout exit call getkey push ax cmp al,#0x20 ; compare to Space jb boot3 ; no echo if ctrl char call display ; echo boot3: call crlf pop ax pop bx ; restore image descriptor ptr cmp al,#0x79 ; y is yes je boot9 cmp al,#0x59 ; Y is yes je boot9 vmwto: br iloop boot9: #endif test byte ptr (bx+id_flags),#FLAG_PASSWORD ; use a password jz toboot ; no -> boot test byte ptr (bx+id_flags),#FLAG_RESTR ; restricted ? jz dopw ; no -> get the password cmp byte ptr (si),#0 ; are there any options ? jne dopw ; yes -> password required toboot: br doboot ; ... dopw: #if defined(CRC_PASSWORDS) || defined(SHS_PASSWORDS) push bx ; save the image descriptor ;; BEG_FS ;; SEG_FS mov word ptr par2_timeout,#0xffff ; cancel timeout ;; END_FS mov bx,#msg_pw ; display a prompt call say push bp ; save BP mov bp,sp ; save SP in BP sub sp,#CL_LENGTH ; allocate space for PW string mov si,sp ; si points at string xor di,di ; di counts characters pwloop: #ifdef DEBUG pusha mov ax,si call wout mov al,#32 call display mov ax,di call wout call crlf popa #endif mov cx,#pwtime ; get timeout exit call getkey cmp al,#13 ; CR ? je pwcr ; yes -> handle it cmp al,#21 ; ^U ? je pwdell ; yes -> erase line cmp al,#24 ; ^X je pwdell cmp al,#8 ; BS ? je pwdelch ; yes -> erase one character cmp al,#127 ; DEL je pwdelch ja pwloop ; ignore other non-printable characters cmp al,#32 jb pwloop cmp di,#CL_LENGTH ; check for buffer overflow jae pwloop ; ingnore further input seg ss mov (si),al ; store char in buffer inc si inc di mov al,#42 ; echo '*' call display jmp pwloop ; loop back for more pwdelch: or di,di jz pwloop call pwbs dec si dec di jmp pwloop pwdell: inc di pwdel: dec di jz pwloop call pwbs dec si jmp pwdel pwbs: mov bx,#bs call say ret pwcr: xor cx,cx ; signal okay pwtime: ; CX != 0 if enter here inc cx call crlf sub si,di ; point SI at start of buffer push es ; save ES #if !defined(SHS_PASSWORDS) mov bx,(bp+2) ; restore image descriptor pointer push ss pop es ; ES:SI points at char string #if MAX_PW_CRC>=1 push dword #CRC_POLY1 call crc32 #ifdef DEBUG pusha push ax mov ax,di call wout mov al,#32 call display pop ax push eax call dout call crlf popa #endif cmp eax,(bx+id_password_crc) jne pwcleanup #endif ; insert other checks in here #if MAX_PW_CRC>=2 push dword #CRC_POLY2 call crc32 cmp eax,(bx+id_password_crc+4) jne pwcleanup #endif #if MAX_PW_CRC>=3 push dword #CRC_POLY3 call crc32 cmp eax,(bx+id_password_crc+8) jne pwcleanup #endif #if MAX_PW_CRC>=4 push dword #CRC_POLY4 call crc32 cmp eax,(bx+id_password_crc+12) jne pwcleanup #endif #if MAX_PW_CRC>=5 push dword #CRC_POLY5 call crc32 cmp eax,(bx+id_password_crc+16) jne pwcleanup #endif ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; dec cx ; signal all okay #else /* SHS_PASSWORDS */ ; DI is the count ; SS:SI is the password string push di push si call _shsInit call _shsUpdate call _shsFinal mov bx,(bp+2) ; restore image descriptor pointer lea di,(bx+id_password_crc) mov si,#shs_digest mov cx,#MAX_PW_CRC*4 ; ES==DS repe cmpsb pop si ; restore buffer ptr pop di ; clear stack push ss pop es ; ES=SS je pwcleanup ; CX will be 0 inc cx ; CX is > 0 #endif /* !defined(SHS_PASSWORDS) */ pwcleanup: push cx mov cx,#CL_LENGTH mov di,si xor ax,ax rep ; wipe out password in memory stosb pop cx pop es ; restore the saved ES mov sp,bp pop bp pop bx or cx,cx ; test CX==0 means all okay jz doboot ; fall into pwfail #else push bx ; save the image descriptor lea si,(bx+MAX_IMAGE_NAME+1) ; get a pointer to the password string mov bx,#msg_pw ; display a prompt call say pwagain:xor cl,cl ; CL counts characters after a mismatch pwloop: push cx ; get a key mov cx,#pwtime call getkey pop cx cmp al,#13 ; CR ? je pwcr ; yes -> handle it cmp al,#21 ; ^U ? je pwdell ; yes -> erase line cmp al,#24 ; ^X je pwdell cmp al,#8 ; BS ? je pwdelch ; yes -> erase one character cmp al,#127 ; DEL je pwdelch ja pwloop ; ignore other non-printable characters cmp al,#32 jb pwloop or cl,cl ; counting bad input ? jnz pwbad ; yes -> do it cmp al,(si) ; correct input ? je pwgood ; yes -> go on pwbad: inc cl ; count error jnz pwgood ; no overflow -> go on dec cl ; adjust it jmp pwcr ; terminate input pwgood: inc si ; good character -> go on jmp pwloop pwdell: pop si ; reset the pointer push si add si,#MAX_IMAGE_NAME+1 jmp pwagain ; get password again pwdelch:pop bx ; at the beginning of the line ? push bx add bx,#MAX_IMAGE_NAME+1 cmp si,bx je pwloop ; yes -> ignore it dec si ; remove one character sub cl,#1 jnc pwloop ; no underflow -> go on inc cl ; adjust it jmp pwloop ; next character pwtime: pop cx ; drop CX ... mov cl,#1 ; ... and fail pwcr: call crlf pop bx ; restore the image descriptor or cl,cl ; no errors ? jnz pwfail ; no -> fail cmp byte ptr (si),#0 ; at end ? je doboot ; yes -> continue booting #endif /* CRC_PASSWORDS */ pwfail: mov bx,#msg_pf ; display an error message call say br iloop ; get next input ! Boot the image BX points to doboot: mov byte ptr prechr,#61 ; switch to equal sign push bx ; save image descr mov bx,#msg_l ; say hi call say pop bx ; display the image name push bx call say pop si push si add si,#id_start ; form address ; Now load the kernel sectors xor ax,ax mov word ptr (gdt+0x1b),ax ; set GDT to "load low" mov byte ptr (gdt+0x1f),al mov moff,ax ; map is not loaded yet lodsw ; address of the first map sector xchg cx,ax lodsw xchg dx,ax lodsb push si ; save SI #ifdef DEBUG push ax ; mov bx,#step0 call say pop ax ; #endif mov bx,[map] ; load the first map sector call sread #ifdef DEBUG mov bx,#step0b call say #endif mov bx,#DFLCMD ; load the default command line ;BEG_FS ;SEG_FS mov cx,mt_dflcmd+KEYTABLE+256 ;SEG_FS mov dx,mt_dflcmd+2+KEYTABLE+256 ;SEG_FS mov al,mt_dflcmd+4+KEYTABLE+256 ;END_FS call cread push word ptr (DFLCMD) ; push magic number mov bx,#DFLCMD ; load the fallback sector call load1 pop ax ; valid magic number ? #ifndef LCF_READONLY cmp ax,#DC_MAGIC je dclok ; yes -> can write cmp ax,#DC_MGOFF jne nofbck ; invalid -> must not write dclok: mov bx,#DFLCMD ; fallback data present ? cmp word ptr (bx),#DC_MAGIC jne nofbck ; no -> go on call cmd_write ; write out the command line nofbck: #endif #ifdef DEBUG mov bx,#step1 call say #endif mov bx,#DFLCMD ; load the options sector call load1 mov si,cmdbeg ; copy non-options part of command line mov di,#PARMLINE mov cx,#CL_LENGTH-1 ; max number of characters to copy cpnocl: #if DNAME cmp si,#cmdline #else cmp si,options ; at beginning of options ? #endif je cpnodn ; yes -> go on movsb ; copy one byte loop cpnocl ; next one jmp cpovfl ; signal overflow cpnodn: #if DNAME pop ax ; get saved pointer pop si ; get saved descriptor push si push ax cpdname: lodsb or al,al jz cpdname9 stosb dec cx jmp cpdname cpdname9: #endif mov si,#DFLCMD ; constant options ? cmp byte ptr (si),#0 je nocopt ; no -> go on mov al,#32 ; add a space stosb dec cx ; count character jz cpovfl cpcodsp: #if DEBUG_INITRD cmp dword ptr (si),#0x3d647269 ; "ird=" #else cmp dword ptr (si),#0x3d6d656d ; "mem=" #endif jne cpnotmem call getmem ; get the user-provided memory limit cpnotmem: lodsb ; fetch next byte cmp al,#32 ; space ? je cpcodsp ; yes -> discard it cpcolp: or al,al ; NUL ? jz cpcodn ; yes -> done stosb ; store byte dec cx ; count character jz cpovfl cmp al,#32 ; a space ? je cpcodsp ; yes -> discard next lodsb ; get next byte jmp cpcolp cpcodn: seg es cmp byte ptr (di-1),#32 ; last was space ? jne nocopt ; no -> go on dec di ; discard it inc cx ; ** nocopt: mov si,options ; append variable options cpvalp: lodsb ; copy one byte stosb or al,al ; NUL ? jz cpdone ; done? loop cpvalp ; count and loop back cpovfl: mov (di),cl ; CX is zero #if DEBUG_NEW dec cx ; count 1 more character #endif cpdone: #if DEBUG_NEW push cx mov bx,#msg_pl ; parameter line message call say pop cx mov ax,#CL_LENGTH-1 sub ax,cx call wout call crlf #endif #ifdef DEBUG mov bx,#step2 call say #endif mov es,[initseg] ; load the original boot sector xor bx,bx ; load now call load1 pop si ; restore SI lodsw ; get flags bit map xchg bx,ax ; move to BX lodsw ; copy parameters ... VGA mode ... (done) cmp word ptr vgaovr,#VGA_NOCOVR ; VGA mode not overridden on ; command line ? je vganorm ; no -> go on mov ax,vgaovr ; use that value jmp vgaset vganorm:test bx,#FLAG_VGA jz novga vgaset: seg es mov [VGA_SET],ax ; magic offset in the boot sector novga: push bx ; use flags (BX) later test bx,#FLAG_LOCK ; ... lock target ? jnz lockit ; yup -> do it cmp byte ptr dolock,#0 ; did user ask to lock new target ? je nolock ; no -> go on lockit: #ifndef LCF_READONLY mov bx,#lkwbuf ; save the command line mov word (bx),#DC_MAGIC ; push es push si push ds ; pop es ; call cmd_write ; write out the command line pop si pop es #endif nolock: #ifdef DEBUG mov bx,#step3 call say #endif xor cx,cx seg es add cl,[VSS_NUM] ;;; or cx,cx jnz lsetup mov cl,#SETUPSECS ; default is to load four sectors lsetup: mov es,[setupseg] ; load the setup codes #ifdef MEMORY_CHECK mov ax,cx ; number of sectors to AX shl ax,#5 ; convert to paragraphs (9-4) mov bx,es add bx,ax add bx,#STACK>>4 ; allow for stack space in paragraphs mov ax,cs ; cmp bx,ax jbe enough_mem mov bx,#msg_mem ; we are very short on memory call say enough_mem: #endif xor bx,bx ; other operating system) lsloop: push cx call loadopt pop cx loop lsloop #ifdef DEBUG mov bx,#step4 call say #endif pop bx ; get flags test bx,#FLAG_MODKRN ; "modern" kernel ? jz loadlow ; no -> avoid all patching and such seg es ; set loader version mov byte ptr (16),#LOADER_VERSION test bx,#FLAG_LOADHI ; load kernel high jz nohigh seg es mov ax,word ptr (20+1) ; get start address 00 1000 00 mov (gdt+0x1b),ax seg es mov al,byte ptr (20+3) ; get hi-byte of address mov (gdt+0x1f),al nohigh: seg es ; version >= 1 ? cmp word ptr (6),#NEW_HDR_VERSION jbe noheap ; no -> do not patch heap mov ax,cs sub ax,[initseg] ; find no. of paragraphs available shl ax,4 add ax,#SLA_SIZE_DYN seg es mov word ptr (36),ax seg es ; patch flags or byte ptr (17),#LFLAG_USE_HEAP noheap: pop si ; restore pointer to DESCR to load push [gdt+0x1b] mov al,[gdt+0x1f] push ax call load_initrd ; load the initrd & patch header pop ax mov [gdt+0x1f],al pop bx mov [gdt+0x1b],bx cbw or ax,bx ; load low ? je loadlow ; yes -> do it xor ax,ax ; GDT is already set up ... mov es,ax mov bx,#gdt #if DEBUG_NEW push bx mov bx,#msg_high call say pop bx #endif call lfile ; load the system ... jmp launch2 ; ... and run it loadlow: #if DEBUG_NEW push bx mov bx,#msg_low call say pop bx #endif call loadfile ; load the system launch2: jmp launch ; go ! loadfile: push #SYSSEG ; load a file at SYSSEG:0000 pop es xor bx,bx lfile: call load jmp lfile ! Load one sector. Issue an error at EOF. load1: call loadit ; load the sector mov bx,#msg_eof ; we only get here at EOF call say br restrt loadit: call load ; load it pop ax ; drop return address of load1 ret ! Load one sector. Start the system at EOF. loadopt:call loadit ; load the sector jmp launch ; go ! Load one sequence of sectors. Leave outer function at EOF. load: push es ; save ES:BX push bx lfetch: mov si,moff ; get map offset mov bx,[map] mov cx,(bx+si) ; get address mov dx,(bx+si+2) mov al,(bx+si+4) or cx,cx ; at EOF ? jnz noteof ; no -> go on or dx,dx jnz noteof pop bx ; restore ES:BX pop es pop ax ; pop return address ret ; return to outer function noteof: add si,#sa_size ; increment pointer mov moff,si cmp si,#SECTOR_SIZE - sa_size + 1 ; page end ? jb near doload mov moff,#0 ; reset pointer push cs ; adjust ES pop es mov bl,hinib ; this might get clobbered push bx ; so save it mov bx,[map] ; load map page call sread pop ax ; restore the hi-nibble mov hinib,al ; mov al,#0x2e ; print a dot call display jmp lfetch ; try again ! Start the kernel launch: ; terminate emulation if CD boot test byte ptr [par2_flag2],#FLAG2_EL_TORITO ; a CD? jz not_el_torito mov si,#Map ; empty command packet mov byte ptr (si),#0x13 ; size of command packet mov ax,#0x4b00 ; terminate emulation ;;;; mov dl,al ; DL is 0 mov dl,[init_dx] ; terminate boot device int 0x13 not_el_torito: #ifdef MENU call menu_exit ; make the menu area vanish #endif call crlf ; display a CRLF /* 'outb' was removed in 22.5.5; but the HW stop is required by some BIOSs */ mov dx,#0x3f2 ; stop the floppy motor xor ax,ax out dx,al ; outb mov dl,al int 0x13 ; reset the FDC (AH=0) mov es,[initseg] ; adjust segment registers mov di,#PARMLINE ; set parameter line offset mov ax,cs ; find where we are loaded sub ax,[initseg] ; find no. of paragraphs available shl ax,4 ; convert para. to bytes add di,ax seg es cmp dword ptr CL_HEADER_ID,#0x53726448 ; "HdrS" (reversed) je chkver ; go check header version mbchain: ! it must be the chain loader #ifdef LCF_BDATA BEG_FS SEG_FS ; suppress BIOS data collection or byte ptr par1_prompt+SSDIFF,#FLAG_NOBD ; suppress BIOS data collection END_FS #endif ; ES:DI will point at param line (chain.b) push ds ; save DS mov ds,[setupseg] ; point at chain loader(?) header ; DS points at chain loader cmp dword [parC_signature],#EX_MAG_HL ; jne not_chain cmp word [parC_stage],#STAGE_CHAIN jne not_chain cmp word [parC_version],#VERSION jne not_chain mov dx,[parC_drive] ; get drive ;;; call map_device ; map drive -- uses CS to address "devmap" mov [parC_drive],dl ; store mapped drive mov [parC_devmap],#devmap ; save our drive mapping mov [parC_devmap+2],cs ; our DS register not_chain: pop ds BEG_FS SEG_FS mov dx,[EX_OFF+6] ; pass DX from first stage END_FS #if DEBUG_NEW mov bx,#nohdrs call say jmp cl_wait #else br start_setup2 #endif chkver: mov bh,[gdt+0x1f] ; check for kernel/initrd conflict shl ebx,#8 mov bx,[gdt+0x1b] ; form kernel final load address shl ebx,#8 mov eax,[rdbeg] ; initrd beg address (0 if none) or eax,eax jz no_overwrite sub eax,ebx jae no_overwrite mov bx,#msg_confl br zz no_overwrite: #if DEBUG_NEW mov bx,#hdr1 call say seg es mov ax,CL_HDRS_VERSION call wout mov bx,#hdr2 call say push di #endif seg es cmp word ptr CL_HDRS_VERSION,#NEW_VERSION ; check for ; new cmdline protocol jb protocol201 ! and now the new protocol mov ax,es ; form long address movzx edx,ax ; zero extend segment part to EDX movzx edi,di ; zero extend offset shl edx,4 ; make segment into address add edx,edi ; form long absolute address seg es mov CL_POINTER,edx ; and pass the address #if DEBUG_NEW push edx call dout jmp cl_wait #else jmp start_setup #endif ! the old command line passing protocol protocol201: seg es mov CL_MAGIC_ADDR,#CL_MAGIC ; set magic number seg es mov word ptr CL_OFFSET,di #if DEBUG_NEW mov ax,es call wout mov al,#0x3A ; issue colon call display mov ax,di call wout cl_wait: mov bx,#hdr4 call say pop bx push ds push es pop ds call strlen pop ds call wout call crlf call crlf jmp start_setup #endif start_setup: ; kernel boot comes here #if DEBUG_NEW mov bx,#msg_real #ifdef LCF_VIRTUAL call vmtest jnc boot_real_msg_v mov bx,#msg_virtual boot_real_msg_v: #endif #ifdef LCF_NOKEYBOARD call kbtest jc boot_real_msg_k mov bx,#msg_no_keyboard boot_real_msg_k: #endif call say #endif #ifdef LCF_BDATA mov bx,#msg_bc call say BEG_FS SEG_FS ; suppress BIOS data collection? test byte ptr par1_prompt+SSDIFF,#FLAG_NOBD ; suppress? END_FS jz start_setup3 mov bx,#msg_by call say jmp start_setup2 start_setup3: #ifndef LCF_READONLY or byte ptr [KEYTABLE+256+mt_flag],#FLAG_NOBD ; suppress #if DEBUG_NEW call crlf #endif call kt_write #endif #if DEBUG_NEW ;;; call pause mov ah,#2 ; get keyboard flags int 0x16 and al,#0x70 ; Caps, Num, Scroll Lock flags cmp al,#0x70 je near zzz ; fail with all 3 on #endif BEG_FS SEG_FS mov dx,[EX_OFF+6] ; pass in DX from first stage END_FS push es ; save ES call is_prev_mapper ; is there a previous mapper jz no_remove seg es mov word (di),#0 ; sterilize it no_remove: pop es ; and restore ES call io_biosdata mov bx,#msg_s call say #ifndef LCF_READONLY ; if the BIOS data collection was successful, do not suppress it on future boots and byte ptr [KEYTABLE+256+mt_flag],#~FLAG_NOBD ; no suppress call kt_write #endif #endif /* ifdef LCF_BDATA */ start_setup2: ; chain loader boot comes here #if DEBUG_NEW call pause ; last chance to use the timer #else mov ax,#1500/55 ; about 1.5 second call setto ; set timeout vpaus1: test byte ptr timeout,#-1 jz vpaus1 #endif call remto ; free timer interrupt push es ; is initseg pop ds ; DS = 0x9000 (initseg) #if 0 push es pop fs push es pop gs #endif add sp,#SETUP_STACK_DYN ; increase stack size over this code if ~*&1 ; align to an odd memory location nop endif jmpi 0,SETUPSEG ; segment part is a variable setupseg = *-2 ; setupseg is filled in now initseg: .word INITSEG ! Load one sector (called from load) doload: pop bx ; restore ES:BX pop es ! Load a sequence of sectors, possibly moving into "high memory" (> 1 MB) ! afterwards. xread: push ax ; ES == 0 ? mov ax,es or ax,ax pop ax jz rdhigh ; yes -> read into high memory #ifdef DEBUG br sread #else jmp sread #endif rdhigh: push bx ; okay - DS:BX points to GDT in this case mov bx,#LOADSEG ; adjust ES:BX mov es,bx xor bx,bx call sread ; load the sector(s) mov tempal,al pop bx ; get pointer to GDT push ax ; just in case ... push cx push si mov si,bx ; turn ES:SI into pointer to GDT push ds pop es xor cx,cx ; number of words to move mov ch,tempal #ifdef DEBUG push si push bx push cx mov al,(si+0x14) call bout mov ax,(si+0x12) call wout mov bx,#mov_ar call say mov ah,(si+0x1f) mov al,(si+0x1c) call wout mov ax,(si+0x1a) call wout mov bx,#mov_sz call say pop ax push ax call wout call crlf pop cx pop bx pop si #endif push [gdt+0x1e] push bx ; do the transfer. (save BX, CX and SI because push cx ; we are paranoid) push si mov ah,#0x87 ; Move Extended Memory Block int 0x15 pop si pop cx pop bx jc badmov ; failed ... pop ax ; check the GDT cmp ah,[gdt+0x1f] ; catch a BIOS that does not handle 386 ; addresses (>16Mb) jne badmov+1 ; AH error code will be hi byte of address shr cx,#8-1 ; convert words to bytes/256 sub ax,ax ; put ES back to 0 add (si+0x1b),cx adc (si+0x1f),al mov es,ax ; put ES back to 0 pop si pop cx pop ax ret ; done badmov: pop bx ; discard GDT push ax ; save the error code mov bx,#msg_bm ; tell the user ... jmp reset ; (standard procedure calls say & bout) ! Load a sequence of sectors sread: push bx ; save registers push cx push dx call cread mov di,ax ; save AL return count jc rerror ; error -> complain pop dx ; restore registers pop cx rokay: pop bx shl ax,8 ; convert sectors to bytes add ah,ah jc dowrap ; loaded an entire segment -> advance ES add bx,ax ; move BX jnc nowrap ; same segment -> go on dowrap: mov ax,es ; move ES add ax,#0x1000 mov es,ax nowrap: mov ax,di ; restore the block count in AL aret: ret ; done ! Read error - try a second time and give up if that fails too rerror: push ax mov bx,#msg_re ; say something reset: call say pop ax ; display the error code mov al,ah call bout call crlf ; a CR/LF mov moff,#0 ; restore initial state #if DEBUG_NEW pop ax call swout ; DX pop ax call swout ; CX pop ax call swout ; BX call crlf #endif br restrt ! Convert character in AL to upper case upcase: cmp al,#0x61 ; lower case character ? ('a') jb nolower ; no -> go on cmp al,#0x7a ; 'z' ja nolower sub al,#0x20 ; convert to upper case nolower:ret ; done pause: #if !DEBUG_NEW pusha mov ax,#3200/55 ; delay 3+ seconds call setto delay1: test byte ptr timeout,#-1 jz delay1 popa #else pusha mov ah,#0x86 ; delay in microseconds mov cx,#5<<4 ; 3 seconds; forget DX int 0x15 popa #endif ret #if DEBUG_NEW ! display a double word, pushed into the stack dout: push bp mov bp,sp push ax mov ax,(bp+6) ; get high order call wout mov ax,(bp+4) ; get low order call wout pop ax leave ret 4 ! display a space, followed by a word in AX swout: push ax mov al,#32 call display pop ax ; and fall into wout ! Display a hexadecimal word/byte/nibble wout: push ax xchg al,ah call bout pop ax ; must fall into bout #endif bout: push ax ; save byte shr al,#4 ; display upper nibble call nout pop ax nout: and al,#0x0F ; lower nible only daa ; smaller conversion routine add al,#0xF0 adc al,#0x40 ; AL is hex char [0..9A..F] jmp display ; display it ! part of the 'say' routine ! actual entry point is below at 'say:' say_loop: cmp al,#10 ; \n ? jne nonl ; no -> go on mov al,#13 ; display a CRLF call display mov al,#10 nonl: cmp al,#12 ; ^L ? jne nocls ; no -> go on #ifdef MENU call menu_form_feed ; simulate a FF #else #ifdef BITMAP cmp BYTE [abs_cx+1],#0 ; graphic screen on? jne tosnext #endif push bx mov ah,#0xf ; clear the local screen int 0x10 xor ah,ah int 0x10 pop bx #endif tosnext: jmp snext ; next character nocls: call display ; display, tty-style snext: inc bx ; next one ! fall into say ; process next character ! Display a NUL-terminated string on the console say: mov al,(bx) ; get byte or al,al ; NUL ? jnz say_loop ; not the end ret ! Display CR/LF crlf: mov al,#13 ; CR call display mov al,#10 ; LF ;;; jmp display ; fall into display ! Display one character on the console display: push bx ; save BX #ifndef LCF_NOSERIAL call serdisp #endif #if defined(MENU) || defined(BITMAP) seg cs cmp word [suppress],#0 jnz dispret #ifdef MENU push ds push cs pop ds push dx cmp byte [abs_cx+1],#0 ; is special scrolling in effect? je goshowit ; jump if no special handling call mn_getcursor ; get cursor pos. in DX cmp al,#8 ; is it BS jne scroll1 or dl,dl ; at col. 0? jne goshowit ; must simulate a BS mov dl,[mn_max_row_col] ; move to EOL mov al,#0x0a ; change to LF dec dh ; back up first of two lines jmp scroll_set ; set new cursor pos. & ring BEL scroll1: cmp al,#0x0a ; test for LF / NL jne scroll2 cmp dh,[mn_max_row_col+1] ; bottom row jae scrollit scroll2: cmp al,#0x20 ; printing char? jb goshowit cmp dx,[mn_max_row_col] ; bottom corner jne goshowit scrollit: pusha mov ax,#0x601 ; scroll up 1 line mov bh,[mn_at_mono] mov cx,[abs_cx] mov dx,[mn_max_row_col] int 0x10 ; do the scroll popa scroll_set: dec dh call mn_setcursor ; set cursor up 1 row goshowit: pop dx pop ds #endif #endif #ifndef LCF_NOVGA ;;; xor bh,bh ; display on screen mov bx,#7 ; set color for TEXT interface mov ah,#14 int 0x10 #endif dispret: pop bx ; restore BX ret #ifndef LCF_NOSERIAL serdisp:push dx ; wait for space in the send buffer seg cs mov dx,slbase or dx,dx jz serret add dx,#5 push ax serwait:in al,dx test al,#0x10 ; break -> set break flag jz nobrk seg cs mov byte ptr break,#1 nobrk: test al,#0x20 ; ready to send ? jz serwait ; no -> wait sub dx,#5 ; send the character pop ax out dx,al serret: pop dx ; done ret #endif ! Get a key (CX = timeout exit) getkey: ;; BEG_FS ;; SEG_FS ; set the timeout mov ax,par2_timeout ;DSC_OFF-10+SSDIFF ;; END_FS call setto gwtkey: mov ah,#1 ; is a key pressed ? int 0x16 jnz gotkey ; yes -> get it #ifndef LCF_NOSERIAL mov dx,slbase ; using a serial port ? or dx,dx jz gnokey ; no -> wait add dx,#5 ; character ready ? in al,dx test al,#1 jz gnokey ; no -> wait sub dx,#5 ; get it in al,dx and al,#0x7f ; strip 8th bit jnz gotch ; ignore NULs #endif gnokey: #if defined(MENU) || defined(BITMAP) #ifdef BITMAP cmp byte [abs_cx+1],#0 je no_timer_display #endif call timer_display no_timer_display: #endif test byte ptr timeout,#1 ; timed out ? jz gwtkey ; no -> wait pop ax ; discard return address jmp cx ; jump to timeout handler gotkey: xor ah,ah ; read a key int 0x16 push bx ; keyboard translation (preserve BX) mov bx,#KEYTABLE xlatb pop bx gotch: #ifdef LCF_ONE_SHOT BEG_FS SEG_FS ; always enter prompt ? test byte ptr par1_prompt+SSDIFF,#FLAG_PROMPT END_FS jz noosht ; yes -> do not disable timeout #endif ; disable timeout test byte ptr par2_flag2,#FLAG2_UNATTENDED jnz nocancel mov word ptr par2_timeout,#0xffff nocancel: noosht: ret ; done ! Shift wait loop (AX = timeout, returns CY set if interrupred) waitsh: call setto ; set timeout actlp: mov ah,#2 ; get shift keys int 0x16 #if defined(LCF_VIRTUAL) && DEBUG_NEW and al,#0x1f ; anything set ? (except NumLock or CapsLock) #else and al,#0x5f ; anything set ? (except NumLock) #endif jnz shpress ; yes -> return with CY set ; 22.7.1 begin mov ah,#1 ; get status int 0x16 jnz shpress ; key pressed ; 22.7.1 end #ifndef LCF_NOSERIAL mov dx,slbase ; using a serial port ? or dx,dx jz acnosp ; no -> go on cmp byte ptr break,#0 ; break received ? jnz shpress ; yes -> return with CY set add dx,#5 ; check for pending break in al,dx test al,#0x10 jnz shpress ; break received -> return with CY set #endif acnosp: test byte ptr timeout,#1 ; timed out ? jz actlp ; no -> wait clc ; clear carry ret ; done shpress:stc ; set carry ret ; done ! Timeout handling instto: push ds ; install the timeout handler push #0 pop ds cli ; no interrupts mov eax,[0x1c*4] ; get the old vector seg cs mov [int1c_l],eax ; save H & L parts mov [0x1c*4],#tick ; install new vector mov [0x1c*4+2],cs sti ; done pop ds ret remto: push es ; remove the interrupt handler push #0 pop es mov eax,[int1c_l] ; restore the old vector seg es mov [0x1c*4],eax ; ** pop es ret ! AX = ticks, 0xffff = no timeout setto: or ax,ax ; time out immediately ? jz toimmed ; yes -> do it cli ; set timeout value mov cntdown,ax mov byte ptr timeout,#0 ; clear timed-out flag sti ; done ret toimmed:mov byte ptr timeout,#0xff ; set the timed-out flag ret ; done tick: pushf ; save flags seg cs ; no timeout ? cmp word ptr cntdown,#0xffff je notzro ; yes -> go on seg cs ; decrement counter dec word ptr cntdown jnz notzro ; not zero -> go on seg cs ; set timeout flag mov byte ptr timeout,#0xff notzro: seg cs push dword [int1c_l] iret ; continue with old interrupt kt_set: ;; BEG_FS ;; SEG_FS ; load the keyboard translation table mov cx,par2_keytab ;MSG_OFF+SSDIFF+7 ;; SEG_FS mov dx,par2_keytab+2 ;MSG_OFF+SSDIFF+9 ;; SEG_FS mov al,par2_keytab+4 ;MSG_OFF+SSDIFF+11 ;; END_FS mov bx,#KEYTABLE ret #ifndef LCF_READONLY ! Sector write; used for the keytable only kt_write: push es push ds pop es call kt_set ; set for KEYTABLE i/o BEG_FS SEG_FS ; BIOS data collection worked before? test byte ptr par1_prompt+SSDIFF,#FLAG_BD_OKAY END_FS jnz kt_nowrite test byte ptr [par2_flag2],#FLAG2_EL_TORITO ; a CD? jnz kt_nowrite call cwrite kt_nowrite: pop es ret ! Sector write; used for the stored command line only cmd_write: ;BEG_FS ;SEG_FS mov cx,mt_dflcmd+KEYTABLE+256 ;SEG_FS mov dx,mt_dflcmd+2+KEYTABLE+256 ;SEG_FS mov al,mt_dflcmd+4+KEYTABLE+256 ;END_FS ; fall into cwrite ; ; General sector write ; cwrite: #ifdef FLAG_RAID_NOWRITE BEG_FS SEG_FS test byte ptr par1_prompt+SSDIFF,#FLAG_RAID_NOWRITE ; no writes? END_FS jnz cwok ; jump if no writing allowed #endif BEG_FS SEG_FS test byte ptr par1_prompt+SSDIFF,#FLAG_RAID ; is it a RAID write END_FS jnz cmd_raid_wrt mov byte ptr (dsk_wrflag),#WR_ENABLE ; flag write operation call cread mov byte ptr (dsk_wrflag),#0 ; flag read operation jnc cwok ; no error - return cnok: pusha cmp ah,#3 ; write protect error je cnok3 push ax ; save error code in AH mov bx,#msg_wrerr call say pop ax mov al,ah ; error code call bout call crlf ; leave space jmp cnok5 cnok3: mov bx,#msg_wrerr3 ; write protect call say cnok5: popa stc ; flag error JRC cwok: #if DEBUG_NEW pushf call pause popf #endif ret ; done cmd_raid_wrt: test dl,#RAID_REL_FLAG ; relocation called for? jnz crw1 mov ah,#0x99 ; flag error jmp cnok crw1: push si mov si,[rmask] ; get raid physical device mask cwrm = LINEAR_FLAG|LBA32_FLAG|LBA32_NOCOUNT|RAID_REL_FLAG|0X80 and dl,#cwrm ; save flags, set device to 80 cwr2: shr si,#1 jnc cwr3 pusha mov byte ptr (dsk_wrflag),#WR_ENABLE ; flag write operation call cread_physical ; read the PHYSICAL device # mov byte ptr (dsk_wrflag),#0 ; flag read operation popa cwr3: inc dx or si,si ; clears the carry jnz cwr2 pop si ;;; clc ; signal no error jmp cwok cwr_cnt: .byte 0 ; device code count cwr_flags: .byte 0 ; saved flags #endif kt_read: ; KEYTABLE read call kt_set ; set for KEYTABLE i/o call cread jc keyerr mov si,#KEYTABLE ; compute a checksum of the keytable mov di,#SECTOR_SIZE - 8 ; skip the last 4+4 bytes push dword #CRC_POLY1 call crc32 add di,si cmp eax,dword (di) jz nokeyerr ! Checksum error keyerr: mov bx,#msg_chkkey br zz ; go wait nokeyerr: ret ! Sector read ! enter with AL, CX, DX, ES, BX set for read ! trashes CX and DI ! cread: ; entry point for mapped device r/w call map_device ; DL (logical) -> DL (physical) cread_physical: ; same entry, device is not mapped test dl,#LINEAR_FLAG|LBA32_FLAG jnz use_linear push ax ;save the count mov ah,#2 ;read command call dsk_do_rw ; int 0x13 with retries pop cx ;Carry Set means error on read mov al,cl ;count in AL, error code in AH ret use_linear: mov ah,hinib ;will be zero for LINEAR xchg al,dh ;AX is possible address test dl,#LBA32_FLAG ;test for LBA32/LINEAR ***** jz lnread ;pure LINEAR ***** test dl,#LBA32_NOCOUNT jz lnread mov ah,dh ;former count is really hi-nibble mov hinib,ah mov dh,#1 ;set count to 1 lnread: xchg di,ax ;hi-address to DI mov al,dh ;count to AL test dl,#RAID_REL_FLAG ; ****** jz ln_do_read ; ****** call translate ; in volume.S ln_do_read: call lba_read mov al,cl ;count returned in AL, error code in AH ret ;Carry Set means error on read #ifdef LCF_VIRTUAL ; vmtest -- return Carry=1 if in virtual (VMware) mode ; return Carry=0 if in real mode ; vmtest: #ifndef LCF_SUSPEND pushad ; save all extended registers smsw ax rcr al,1 ; PE bit in AL to Carry jc vm_ret ; exit if virtual mode #if DEBUG_NEW mov ah,#2 ; get keyboard flags int 0x16 and al,#0x50 ; Caps, Scroll Lock flags cmp al,#0x40 je vm_vir ; Caps only means virtual boot simulated #endif ; ; If no vmdefault, vmdisable, or vmwarn keywords were used, then we do not ; care about virtual mode. Do not touch the hardware, and always return ; Carry=0. ; test byte ptr [par2_flag2],#FLAG2_VIRTUAL ; any vmXXX keywords? jz vm_ret ; TEST clears the carry, always ; ; VMware(R) test for virtual mode ; mov eax,#0x564D5868 ; EAX: in = 'VMXh' out = version xor ebx,ebx ; EBX: out = 'VMXh' under vmware mov edi,eax mov dx,#0x5658 ; DX: in = 'VX' mov ecx,#10 ; ECX: in = VMXGetVersion in eax,dx cmp ebx,edi ; test for vmware clc ; NOT vmware if Carry==0 jne vm_ret ; not vmware inc eax ; carry is not affected by INC jz vm_ret ; invalid version number == 0xFFFFFFFF vm_vir: stc ; signal virtual mode vm_ret: popad ; restore all the extended registers ret #else /* LCF_SUSPEND changes the interpretation of "virtual" */ pusha push es test byte ptr [vm_cache],#0xFF ; test cached value jnz vm_ret mov byte ptr [vm_cache],#2 ; not virtual push ds pop es ; ES:BX mov bx,#MAP ; use this buffer mov cx,#1 ; sector 1 mov dx,#0x80 ; dos C: drive mov al,cl ; 1 sector call cread ; jc vm_ret ; not virtual if error mov cx,#PARTITION_ENTRIES ; count 4 PT entries lea bx,[PART_TABLE_OFFSET](bx) ; first partition entry vm_pt1: test byte ptr (bx),#0x80 ; active jz vm_pt2 cmp byte ptr (bx+4),#LCF_SUSPEND ; suspend partition jne vm_pt2 mov byte ptr [vm_cache],#1 ; suspend is active vm_pt2: lea bx,[PARTITION_ENTRY](bx) ; bump pointer loop vm_pt1 vm_ret: mov al,[vm_cache] ; get cached value shr al,#1 pop es popa ret vm_cache: .byte 0 ; 0=unknown, 1=virtual, 2=non-virtual #endif /* LCF_SUSPEND */ #endif /* LCF_VIRTUAL */ #if LCF_NOKEYBOARD ; kbtest -- return Carry=1 if IBM PC/AT keyboard is present ; -- return Carry=0 if no IBM keyboard is present ; kbtest: push ax ; ; If neither nokbdefault nor nokbdisable was used, we do not touch ; the keyboard hardware. Always report Carry=1 (keyboard present). ; test byte ptr [par2_flag2],#FLAG2_NOKBD jz kbtest8 #if DEBUG_NEW pusha mov bx,#msg_kbtest call say ; tell about keyboard test popa #endif /* mardy */ cli ; added 5/17/2006 mov al,#0xee ; echo command out #0x60,al wait_kbd_ctrl_ready: in al,#0x64 and al,#0x01 jz wait_kbd_ctrl_ready ; read status port while it is not ready in al,#0x60 sti ; added 5/17/2006 xor al,#0xee ; XOR clears the carry jne kbtest9 ; if we got the same byte, the keyboard is attached /* mardy */ #if DEBUG_NEW mov ah,#2 ; get keyboard flags int 0x16 and al,#0x20 ; Num Lock flag jz kbtest9 ; AND cleared the carry #endif kbtest8: stc ; flag keyboard present kbtest9: pop ax ret #endif /* LCF_NOKEYBOARD */ #if 1 ; crc32 -- calculate CRC-32 checksum ; ; call: ; push dword #POLYNOMIAL ; ; ES:SI char string pointer ; DI count of characters ; ; call crc32 ; ; CRC-32 is returned in EAX or DX:AX ; the arguments are popped from the stack ; crc32: push bp mov bp,sp push si push di push bx push cx xor eax,eax ; initialize CRC dec eax ; EAX = 0xFFFFFFFF inc di crc32a: dec di jz crc32d mov cx,#8 ; count 8 bits seg es mov bl,(si) ; get next character inc si crc32b: shl bx,#1 ; get hi bit of char in BH shl eax,#1 ; shift hi bit out of CRC adc bh,#0 ; add carry to BH shr bh,#1 ; put bit in carry jnc crc32c ; skip the xor xor eax,(bp+4) ; xor in the polynomial crc32c: loop crc32b ; loop back for 8 bits jmp crc32a crc32d: not eax ; finialize CRC pop cx pop bx pop di pop si leave ret 4 #endif /* ifdef HIGHMEM_MAX */ ; enter with BX == Ramdisk size (in 4k pages) ; rd_setup: push bx ; save Ramdisk size in pages mov eax,[hma] ; user specified? or eax,eax #ifdef LCF_INITRDLOW jnz rd_have_hma #else /* ifndef LCF_INITRDLOW */ jnz near rd_have_hma BEG_FS SEG_FS test byte ptr par1_prompt+SSDIFF,#FLAG_LARGEMEM END_FS jz near no_e801 ; try the E820 memory map first xor edx,edx ; flag nothing found xor esi,esi ; flag size==0 xor ebx,ebx jmp e8go e8go2: or ebx,ebx ; test for end jz e8go5 e8go: push edx ; save best prospect mov eax,#0xe820 mov edx,#0x534d4150 ;'SMAP' mov ecx,#20 mov di,#memmap int 0x15 ; get memory map pop edx ; restore what we have found so far jc no_e820 cmp eax,#0x534d4150 ;'SMAP' jne no_e820 cmp ecx,#20 jne no_e820 #if 0 mov eax,memmap ; get start mov ecx,memmap+4 ; get high part cmp word memmap+16,#1 ; available? jne e8no1 ; go on to next or ecx,ecx jnz e8go2 cmp eax,#0x100000 ; compare to 1Mb ja e8go2 add eax,memmap+8 ; get final address adc ecx,memmap+12 ; shrd eax,ecx,#10 ; convert to 1k blocks (legacy) cmp eax,#4*1024 ; compare to 4Mb jb no_e820 xchg eax,edx ; save in edx jmp e8go2 e8no1: shrd eax,ecx,#10 ; convert to 1k blocks cmp eax,#1024 ; compare to 1Mb jb e8go2 ; loop if too small cmp eax,edx ; check against HMA jae e8go2 xchg eax,edx ; compensate for buggy BIOS jmp e8go2 ; remember in EDX & loop #else cmp word memmap+16,#1 ; available? jne e8go2 mov eax,memmap+4 ; hi part of start shrd memmap,eax,#10 ; convert start to 1k mov eax,memmap+12 ; hi part of size shrd memmap+8,eax,#10 ; convert to 1k cmp dword memmap,#1024 ; below 1M jb e8go2 ; below 1M, no interest cmp esi,memmap+8 ; check size ja e8go2 ; want largest mov edx,memmap ; start (in 1k) mov esi,memmap+8 ; size (in 1k) add edx,esi ; HMA in 1k jmp e8go2 #endif e8go5: or edx,edx ; find anything? jz no_e820 xchg eax,edx jmp rd_have_hma no_e820: ; above failed, try the older E801 block count interface xor cx,cx ; some BIOSs are buggy xor dx,dx mov ax,#0xe801 ; call stc int 0x15 jc no_e801 or cx,cx jz e801cx mov ax,cx e801cx: or dx,dx jz e801dx mov bx,dx e801dx: movzx ebx,bx movzx eax,ax shl ebx,#6 ; convert 64k to 1k mov ecx,#16*1024 cmp eax,ebx ; compare sizes ja e801eax add ebx,ecx ; add in 16M mov eax,ebx ; and use this value jmp rd_have_hma e801eax: add eax,#1024 ; add 1M cmp eax,ecx ; is it 16M jne rd_have_hma add eax,ebx ; add in ebx jmp rd_have_hma #endif /* ifndef LCF_INITRDLOW */ no_e801: ; above two methods failed, try the old 0x88 function mov ah,#0x88 ; get count of extended memory blocks int 0x15 movzx eax,ax ; extend to dword add eax,#1024 ; add in base 1M ; rd_have_hma: ; have the HMA / 1k in EAX #if DEBUG_NEW push eax shl eax,10 ; convert to address push eax call crlf call dout ; pops its argument mov bx,#msg_hma call say pop eax #endif mov ebx,#15*1024 ; 15Mb cmp eax,ebx ; compare to 15M jbe rd_use_eax ; use lower value BEG_FS SEG_FS test byte ptr par1_prompt+SSDIFF,#FLAG_LARGEMEM END_FS jnz large_okay xchg eax,ebx ; limit to 15Mb large_okay: mov ebx,#HIGHMEM_MAX/1024 #ifdef NEW3_HDR_VERSION push ds mov ds,[initseg] ; load the original boot sector cmp word ptr [CL_HDRS_VERSION],#NEW3_HDR_VERSION jb not203 mov ebx,[CL_RAMDISK_MAX] #if DEBUG_NEW pop cx push cx mov ds,cx pushad push ebx mov bx,#hdr3 call say call dout call crlf popad #endif dec ebx shr ebx,#10 ; divide by 1024 inc ebx not203: pop ds #endif /* ifdef NEW3_HDR_VERSION */ cmp eax,ebx jb rd_use_eax ;;;rd_use_smaller: xchg eax,ebx ; must use the smaller rd_use_eax: pop bx ; get size in pages shr eax,2 ; convert to pages movzx ebx,bx ; zero high part of size sub eax,ebx ; start address of ramdisk to EAX #if DEBUG_INITRD cmp dword ptr [hma],#0x180000/1024 ; 1.5Mb jb drd_use_eax cmp dword ptr [hma],#0xF00000/1024 ; 15Mb jae drd_use_eax mov eax,[hma] ; HMA is initrd start shr eax,2 drd_use_eax: #endif #if 0 cmp eax,#256 ; we probably need more than 1M for the ja rd_okay ; kernel to be useful ... mov bx,#msg_rd ; complain ;;; call say ; is at zz br zz ; and halt #else cmp eax,#4*256 ; Ramdisk loaded below 4Mb jae rd_okay ; kernel to be useful ... mov bx,#msg_rd4M ; complain call say ; is at zz #endif rd_okay: shl eax,4 ; shift (12-8) -> 4 mov [rdbeg+1],ax ; set up beginning address mov [gdt+0x1b],ax ; set the GDT for the moves shr eax,16 ; get hi-byte of address mov [rdbeg+3],al ; set rest of address mov [gdt+0x1f],al ; and in the GDT, too ret /* endif / ifdef HIGHMEM_MAX */ /* enter with SI pointing to DESCRIPTOR DS = CS ES unknown SI - points at the descriptor */ load_initrd: push [map] push [moff] mov word ptr [map],#MAP2 push ds pop es mov ax,(si+id_flags) ; get FLAG_TOOBIG, if any #if FLAG_LARGEMEM!=FLAG_TOOBIG # error "FLAG_LARGEMEM and FLAG_TOOBIG must be equal" #endif and al,#FLAG_TOOBIG ; separate flag BEG_FS SEG_FS or byte ptr par1_prompt+SSDIFF, al ; set FLAG_LARGEMEM END_FS add si,#id_rd_size ; point at ramdisk size long ! take care of the RAM disk first xor eax,eax mov (rdbeg),eax ; clear address lodsd mov (rdszl),eax ; set rdszl+rdszh add eax,#4095 ; round up & shr eax,#12 ; convert to pages xchg bx,ax ; copy to BX lodsw ; address of the first map sector xchg cx,ax lodsw xchg dx,ax lodsb or bx,bx ; no RAM disk ? jz noramd ; yes -> skip it 2 push si ; save SI, ES, and BX (RD size) push es push bx mov bx,[map] ; load the first map sector call sread mov moff,#0 #ifdef DEBUG mov bx,#stepa call say #endif /* ifdef HIGHMEM_MAX */ pop bx call rd_setup /* endif */ cmp dword ptr (rdbeg),#0 je nordpt ; no -> no need to patch header for that ; setrdp: #if DEBUG_NEW push dword (rdbeg) ; print RAM disk address mov bx,#msg_rd2 call say call dout call crlf #endif mov es,[setupseg] ; load the setup codes mov eax,(rdbeg) ; get RAM disk start address seg es mov (24),eax ; store in header mov eax,rdszl seg es mov (28),eax ; set RAM disk size nordpt: push #0 ; ES=0 is our secret code to load via GDT pop es mov bx,#gdt call lfile ; load it #if 1 mov al,#0x20 ; print a space call display #endif pop es ; restore ES and SI pop si noramd: pop [moff] pop [map] ret #ifndef LCF_NOSERIAL serLI: .byte 13,10,0x4c,0x49 ; cr,lf,"LI" BAUD_BASE = 115200 ; divisor == 1 divisor: .byte BAUD_BASE / 19200 ; must be same as bsect.c table .byte BAUD_BASE / 38400 .byte BAUD_BASE / 57600 .byte BAUD_BASE / 115200 .byte BAUD_BASE / 2400 .byte BAUD_BASE / 2400 .byte BAUD_BASE / 2400 .byte BAUD_BASE / 2400 ; serial_setup -- do the setup for the serial line communications ; ; No registers are saved ; serial_setup: ;; BEG_FS ;; SEG_FS mov dx,par2_port ; use a COM port ? ; watch out, loads par2_ser_param ;; END_FS dec dl js nocom ; no -> go on xor ax,ax ; initialize the serial port xchg al,dh push ax push dx ;;; or al,#0x06 ; stop bits = 2, nbits = 7 or 8 ; this OR is not needed yet (21.7) int 0x14 ; Communications Port INIT push #0x40 pop ds pop bx ; was DX shl bx,#1 mov dx,(bx) ; get the port address from the BIOS seg cs ; keep it mov slbase,dx pop bx ; special baud rate test -- was AX test bl,#0x04 ; stop bits == 2? cli ; do not disturb any code below jz stdbps ; standard BPS shr bx,#5 ; index divisor array seg cs mov bl,divisor(bx) spcbps: ; CLI: do not disturb ... push dx ; save base address add dx,#3 ; enable divisor latch access in al,dx or al,#0x80 out dx,al pop dx ; set new divisor push dx xchg ax,bx out dx,al inc dx mov al,ah out dx,al inc dx ; disable divisor latch access inc dx xchg ax,bx and al,#0x7f out dx,al pop dx ; restore base address stdbps: ; CLI: redundant if fell in from above push dx add dx,#4 ; address Modem Control Reg. #if 0 in al,dx or al,#3 ; turn on DTR and RTS #else mov al,#3 ; turn on DTR and RTS #endif out dx,al pop dx sti ; done mov cx,#32 ; drain the queue (if any) drain: in al,dx loop drain add dx,#5 ; clear the status register in al,dx ; send "\r\nLI" to the serial port mov si,#serLI mov cx,#4 ser1: seg cs lodsb call serdisp loop ser1 nocom: ret #endif /* LCF_NOSERIAL */ #ifdef SHS_PASSWORDS #include "shs3.S" #endif #include "read.S" #include "volume.S" #define SECOND_STAGE_LOADER #include "mapper.S" #undef SECOND_STAGE_LOADER #ifdef LCF_BDATA #include "bdata.h" #include "biosdata.S" #endif #if defined(MENU) || defined(BITMAP) || DEBUG_NEW #include "strlen.S" #endif #ifdef MENU #include "graph.S" #include "menu.S" #include "crt.S" #endif #ifdef BITMAP #include "bitmap.S" #include "vesainfo.h" #include "display4.S" #endif ! Put tokens into keyboard buffer putkbd: add si,#4 ; skip over "kbd=" push es xor ax,ax ; set ES to zero mov es,ax pknext: lodsb ; get next byte or al,al ; NUL ? jz pkdone ; yes -> done cmp al,#32 ; blank ? jne pkrd ; no -> read scan code pkdone: dec si ; return last character pop es ; done ret pkrd: xor cx,cx ; clear accumulator pkrdlp: cmp al,#97 ; lower case character ? jb pknol ; no -> go on sub al,#32 ; make upper case pknol: sub al,#48 ; normalize cmp al,#10 ; >"9" ? jb pkok ; no -> okay cmp al,#17 ; <"A" ? jb pksyn ; yes -> syntax error sub al,#7 ; adjust cmp al,#16 ; >"F" ? jae pksyn ; yes -> syntax error pkok: shl cx,1 ; shift CX jc pksyn ; carry means trouble shl cx,1 jc pksyn shl cx,1 jc pksyn shl cx,1 jc pksyn add cl,al ; put in lowest nibble lodsb ; get next byte or al,al ; NUL ? jz pkend ; yes -> at end cmp al,#32 ; space ? je pkend ; yes -> at end cmp al,#44 ; comma ? je pkmore ; yes -> end of token jmp pkrdlp ; token continues pksyn: mov bx,#msg_pks ; complain call say pkfls: lodsb ; flush to end of option or al,al jz pkdone cmp al,#32 je pkdone jmp pkfls pkend: call pkput ; store token jmp pkdone ; ... and return pkmore: call pkput ; store token jmp pknext ; handle next token pkput: seg es ; get buffer pointer mov bx,[KBEND] mov dx,bx add dx,#2 ; increment it cmp dx,#KBHIGH ; (wrap around end) jb pknadj mov dx,#KBLOW pknadj: seg es ; buffer full ? cmp dx,[KBBEG] je pkfull ; yes -> error seg es ; store scan code mov (bx+0x400),cx seg es ; store new pointer mov [KBEND],dx ret ; done pkfull: mov bx,#msg_pkf ; complain call say pop ax ; discard return address jmp pkfls ; abort ! Set VGA mode setvga: add si,#4 ; skip over "vga=" push si ; save SI mov bx,#vgatab ; scan VGA table svgatb: pop si ; get pointer to option value push si mov cx,(bx) ; get VGA code or cx,cx ; at end ? jz vganum ; yes -> must be numeric inc bx ; compare the strings inc bx vgacmp: lodsb call upcase ; (case-insensitive) mov ah,(bx) inc bx or ah,ah ; at end ? jnz vgamore ; no -> go on or al,al ; at end of line ? jz vgafnd ; yes -> found it cmp al,#32 ; space ? je vgafnd ; yes -> found it jmp svgatb ; try next entry otherwise vgamore:cmp al,ah je vgacmp ; equal -> next character vgaskp: mov al,(bx) ; skip to end of reference string inc bx or al,al jnz vgaskp jmp svgatb ; try next entry vgafnd: pop ax ; drop SI vgaput: dec si ; read last character again vgaput1: mov vgaovr,cx ; set VGA mode clc ; okay, done ret vganum: pop si ; get SI #if 1 call strtoul jc vgaerr mov cx,ax or dx,dx jnz vgaerr jmp vgaput1 #else xor cx,cx mov ah,cl test byte ptr (si),#0xff ; no value ? jz vgaerr ; yes -> error vgadig: lodsb ; get the next character or al,al ; at end ? jz vgaput ; yes -> done cmp al,#32 je vgaput cmp al,#48 ; is it a digit ? (0x30=48="0") jb vgaerr ; no -> error cmp al,#57 ; 57=0x39="9" ja vgaerr sub al,#48 ; cx = cx*10+al-'0' imul cx,#10 add cx,ax jnc vgadig ; next one #endif vgaerr: mov bx,#msg_v ; display an error message call say /* ifdef HIGHMEM_MAX */ xor eax,eax mov dword ptr [hma],eax /* endif */ stc ; return an error ret vgatab: #ifdef NORMAL_VGA .word ASK_VGA .ascii "ASK" .byte 0 .word EXTENDED_VGA .ascii "EXTENDED" .byte 0 .word EXTENDED_VGA .ascii "EXT" .byte 0 .word NORMAL_VGA .ascii "NORMAL" .byte 0 #endif .word 0 ! get numeric string suffixed with "KkMmGg" ! updates SI get_K: push cx ; save CX call strtoull ; get number in DX:AX jc gmthis2 ; signal conversion error mov bl,(si) ; get next character or bl,#0x20 ; convert to lower case cmp bl,#0x6b ; 'K' or 'k' ? je gmthis ; yes -> do not change #if 1 mov cx,#20 ; divide or multiply by 2^20 cmp bl,#0x67 ; 'G' or 'g' ? je gmmul #endif mov cx,#10 ; divide or multiply by 2^10 cmp bl,#0x6d ; 'M' or 'm' ? je gmmul ! no Suffix dec si ; will increment later gmdivl: shr eax,cl ; shift by CL jmp gmthis ; done gmmul: gmmull: shl eax,1 ; shift by 1 each time jc gmvbig ; very big if overflow loop gmmull ; ten times ! exit with no error gmthis: inc si clc ; signal no error gmthis2: mov bl,(si) ; next character to BL #if 0 jc gmdebug1 pushad push eax call dout mov bx,#msg_gk call say popad gmdebug1: #endif pop cx ; restore register ret gmvbig: mov eax,#HIGHMEM_MAX/1024 jmp gmthis ! Set memory limit getmem: push si ; save SI for copying add si,#4 ; advance to number? call get_K jc gmcopy ; error, just copy it cmp bl,#0x40 ; is it '@' jne gm22 ! @ format (2.4 kernels) push eax ; save size inc si ; skip '@' call get_K pop edx ; restore size jc memerr cmp eax,#1024 ; start : 1meg ja gmcopy ; just copy if above add eax,edx ; EAX = hma/1024 cmp eax,#2048 ; high : 2meg jbe gmcopy gm22: or bl,#0x20 cmp bl,#0x20 ; NUL or SPACE #if 0 jne memerr #else jne gmcopy ; allow # and $ #endif cmp dword ptr [hma],#0 ; set already? jne gmnocopy mov dword ptr [hma],eax ; set it gmcopy: pop si gmret: ret gmnocopy: pop bx ret memerr: mov bx,#msg_me ; numeric conversion error call say br restrt strtoull: ; numeric conversion to EAX call strtoul push dx push ax pop eax ret strtoul: /* string to unsigned long in DX:AX */ xor ax,ax xor dx,dx mov cx,#10 ; default radix is decimal cmp byte ptr (si),#0x39 ja s2lbad ; error if > '9' cmp byte ptr (si),#0x30 ; == '0'? jb s2lbad ; error if < '0' jne s2lnext inc si dec cx dec cx ; assume octal : CX = 8 cmp byte ptr (si),#0x58 ; == 'X'? je s2lhex cmp byte ptr (si),#0x78 ; == 'x'? jne s2lnext s2lhex: add cx,cx ; it is hexadecimal inc si s2lnext: xor bx,bx mov bl,(si) ; get next character or bl,#0x20 ; convert to lower case sub bl,#0x30 ; - '0' jb s2ldone cmp bl,cl ; compare to radix jb s2lmul add bl,#0x30-0x61+10 cmp bl,cl ; compare to radix jnb s2ldone s2lmul: push dx ; save high order mul cx ; multiply by radix add ax,bx adc dx,#0 ; carry possible only in radix 10 pop bx push dx xchg ax,bx mul cx or dx,dx jnz s2lbad pop dx add dx,ax jc s2lbad xchg ax,bx inc si jmp s2lnext s2lbad: stc ret s2ldone: clc ret /* new 22.7 */ ; find_image ; if there is something on the command line ; return the image number it selects ; ; enter with: ; nothing ; exit with: ; If nothing selected: ; Carry Clear ; AX==0 ; If an image is selected: ; (fuzzy selection or exact selection) ; Carry SET ; AX==#image ; BX==pointer to descriptor ; ; ; side effect: ; The selected image is hi-lited if the menu is displayed ; find_image: push cx push si push di mov cx,#IMAGES ; test all names mov si,#DESCR0 xor bx,bx ; clear BX push si fi_nextn: mov di,#cmdline test byte ptr (si),#0xFF ; null descriptor at end jz fi_nomore fi_nextc: mov al,(si) ; get next character in descr inc si #ifdef LCF_IGNORECASE call upcase #endif mov ah,al mov al,(di) ; get next char in cmdline inc di #ifdef LCF_IGNORECASE call upcase #endif or al,al ; NUL in command line je fi_pmat cmp al,#32 ; SPACE in command line jne fi_cmp ; have partial match, set BX conditionally fi_pmat: or ah,ah ; NUL in descriptor name jz fi_found ; EXACT match found test byte ptr par2_flag2,#FLAG2_UNATTENDED ; (22.7) jnz fi_skipn ; no partial match if unattended or bx,bx jnz fi_skipn ; already set pop bx push bx jmp fi_skipn ; go to next fi_cmp: cmp al,ah ; character equal ? je fi_nextc ; compare next character ; advance to next descriptor fi_skipn: pop si add si,#id_size ; test next name push si loop fi_nextn fi_nomore: pop si or bx,bx ; fuzzy match? jnz fi_fuzzy #if defined(MENU) || defined(BITMAP) mov ax,[dimage] call lowlite ; no match, nothing hi-lited ;;; mov word [dimage],#0 #endif xor ax,ax ; clears the carry fi_exit: pop di pop si pop cx ret fi_found: pop bx ; BX is matched descriptor fi_fuzzy: mov ax,bx sub ax,#DESCR0 mov cl,#id_size div cl cbw #if defined(MENU) || defined(BITMAP) mov di,[dimage] mov [dimage],ax cmp byte [abs_cx+1],#0 ; see if menu is displayed je fi_nochange cmp ax,di je fi_nochange2 xchg ax,di call lowlite xchg ax,di fi_nochange2: call hilite fi_nochange: #endif stc jmp fi_exit /* end new 22.7 */ #if DEBUG_NEW ; check that nobody has changed the FS register fs_check: push ax mov ax,fs seg cs cmp ax,[firstseg] je fs_check_okay ; oops, somebody changed it !!! pop ax ; restore pop ax ; get return push cs pop ds sub ax,#3 ; length of call call wout mov bx,#msg_fs_changed br zz fs_check_okay: #if CHECK_FS_VERBOSE pusha mov bp,sp mov ax,16+2(bp) sub ax,#3 call wout mov bx,#msg_fs_okay push ds push cs pop ds call say pop ds popa #endif fs_check_ret: pop ax ret msg_fs_changed: .ascii " <- loc where FS is clobbered\n" .byte 0 #if CHECK_FS_VERBOSE msg_fs_okay: .ascii " <- FS okay\n" .byte 0 #endif #endif ! Some messages msg_p: .ascii "boot: " .byte 0 msg_l: .ascii "Loading " .byte 0 msg_bc: .ascii "BIOS data check " .byte 0 msg_s: .ascii "successful\n" .byte 0 msg_by: .ascii "bypassed\n" .byte 0 msg_re: .byte 10 .ascii "Error 0x" .byte 0 msg_nf: .ascii "No such image. [Tab] shows a list." .byte 10,0 msg_time: .ascii "O - Timestamp mismatch\n" .byte 0 msg_chkerr: .ascii "O - Descriptor checksum error\n" .byte 0 msg_chkkey: .ascii "O - Keytable read/checksum error\n" .byte 0 msg_confl: .ascii "Kernel and Initrd memory conflict\n" .byte 0 msg_sigerr: .ascii "O - Signature not found\n" .byte 0 msg_me: .byte 10 .ascii "vga/mem= requires a numeric value" .byte 10,0 msg_wrerr: .ascii "\nMap file write; BIOS error code = 0x" .byte 0 msg_wrerr3: .ascii "\nMap file: WRITE PROTECT\n" .byte 0 #ifdef MEMORY_CHECK msg_mem: .ascii "EBDA is big; kernel setup stack overlaps LILO second stage" .byte 10,0 #endif #ifdef LCF_VIRTUAL msg_vmwarn: .ascii "WARNING: Booting in Virtual environment\n" .ascii "Do you wish to continue? [y/n] " .byte 0 #endif #if DEBUG_NEW sdx: .ascii " DX=" .byte 0 msg_pl: .ascii "\nParameter line = " .byte 0 #ifdef LCF_NOKEYBOARD msg_kbtest: .ascii "**PC/AT Keyboard test**\n" .byte 0 #endif #if defined(LCF_VIRTUAL) || defined(LCF_NOKEYBOARD) msg_real: .ascii "Performing REAL MODE boot\n" .byte 0 #ifdef LCF_VIRTUAL msg_virtual: .ascii "Performing VIRTUAL MODE boot\n" .byte 0 #endif #ifdef LCF_NOKEYBOARD msg_no_keyboard: .ascii "Performing NO KEYBOARD DEFAULT boot\n" .byte 0 #endif #endif msg_where: .ascii "\nINIT SETUP CODE SS:SP EX_OFF_DX 2ndDX\n" .byte 0 msg_low: .ascii " low" .byte 0 msg_high: .ascii " high" .byte 0 hdr1: .ascii "\nHeader 0x" .byte 0 hdr2: .ascii " cmdline at " .byte 0 hdr4: .ascii " length = 0x" .byte 0 hdr3: .ascii "\nRamdisk_max " .byte 0 nohdrs: .ascii "\nNo cmdline passed" .byte 0 msg_rd2: .byte 10 .ascii "RAM disk loaded at: " .byte 0 msg_hma: .ascii " is the HMA\n" .byte 0 msg_gk: .ascii " returned by 'get_K'\n" .byte 0 #endif msg_int:.byte 10 .ascii "*Interrupted*" .byte 10,0 msg_eof:.byte 10 .ascii "Unexpected EOF" .byte 10,0 msg_pw: .ascii "Password: " .byte 0 msg_pf: .ascii "Sorry." .byte 10,0 msg_v: .byte 10 .ascii "Valid vga values are ASK, NORMAL, EXTENDED or a " .ascii "decimal number." .byte 10,0 msg_pks:.byte 10 .ascii "Invalid hexadecimal number. - Ignoring remaining items." .byte 10,0 msg_pkf:.byte 10 .ascii "Keyboard buffer is full. - Ignoring remaining items." .byte 10,0 msg_bm: .byte 10 .ascii "Block move error 0x" .byte 0 #if 0 msg_rd: .byte 10 .ascii "Not enough memory for RAM disk" .byte 10,0 #else msg_rd4M: .byte 10 .ascii "Initial ramdisk loads below 4Mb; " .ascii "kernel overwrite is possible." .byte 10,0 #endif ospc: .ascii "O" #ifdef LCF_BEEP .byte 7 #endif #ifdef LCF_VERSION .ascii " " .ascii SA(VERSION_MAJOR) .ascii "." .ascii SA(VERSION_MINOR) .ascii VERSION_EDIT #endif .byte 32,0 bs: .byte 8,32,8,0 #ifdef DEBUG stepa: .ascii " RAM disk," .byte 0 step0: .ascii " map page," .byte 0 step0b: .ascii " fallback," .byte 0 step1: .ascii " options," .byte 0 step1b: .ascii " fallback," .byte 0 step2: .ascii " boot," .byte 0 step3: .ascii " setup," .byte 0 step4: .ascii " system " .byte 0 sax: .ascii "AX=" .byte 0 sbx: .ascii " BX=" .byte 0 scx: .ascii " CX=" .byte 0 ses: .ascii " ES=" .byte 0 sdone: .byte 10 .byte 0 mov_ar: .ascii " -> " .byte 0 mov_sz: .ascii ", words " .byte 0 #endif .even init_dx: .word 0 #if defined(MENU) || defined(BITMAP) suppress:.word 0 ; suppress console output (MUST be word) #endif hinib: .byte 0 ; hi-nibble of address tempal: .byte 0 moff: .word 0 ; map offset map: .word MAP ; map to use cntdown:.word 0 ; count-down timeout:.byte 0 ; timed out dolock: .byte 0 int1c_l:.word 0 ; old timer interrupt int1c_h:.word 0 old_del:.word 0 ; delay before booting nodfl: .word 0 ; action if no defaults are present #ifndef LCF_NOSERIAL slbase: .word 0 ; serial port base (or 0 if unused) break: .byte 0 ; break received flag #endif usrinpm:.byte UI_MAGIC cmdbeg: .word 0 options:.word 0 rdbeg: .word 0,0 ; RAM dist begin address (dword) rdszl: .word 0 ; RAM disk size rdszh: .word 0 vgaovr: .word 0 ; VGA mode overwrite /* ifdef HIGHMEM_MAX */ hma: .word 0,0 ; Highest Memory Address memmap: .word 0,0,0,0,0,0,0,0,0,0 /* endif */ dskprm: .word 0,0,0,0,0,0 .even ; control alignment from here down acmdbeg:.ascii "auto " mcmdbeg:.ascii "BOOT_IMAGE" prechr: .byte 32 ; space: guard double blank supression ; equal sign: variable assignment cmdline:.byte 0 #ifdef BITMAP _line = cmdline+CL_LENGTH ; must be 640 bytes long #endif .org *+4 theend: lkwbuf = cmdline+CL_LENGTH+2 ; this is a word lkcbuf = lkwbuf+2 theend2 = lkcbuf+CL_LENGTH ; lkcbuf is 256 the_end1 = theend+511 theends = the_end1/512 .org theends*512-4 .long X .align 512 max_secondary: #if 0 /* the older version 21 layout */ Map = max_secondary + SECTOR_SIZE Dflcmd = Map + SECTOR_SIZE Map2 = Dflcmd Descr = Dflcmd + SECTOR_SIZE Keytable = Descr + SECTOR_SIZE*MAX_DESCR_SECS_asm ParmBSS = Keytable + SECTOR_SIZE #else /* a possible 22.5.6 layout puts the descriptors last */ Map = max_secondary + SECTOR_SIZE Dflcmd = Map + SECTOR_SIZE Map2 = Dflcmd Keytable = Dflcmd + SECTOR_SIZE Descr = Keytable + SECTOR_SIZE ParmBSS = Descr + SECTOR_SIZE*MAX_DESCR_SECS_asm #endif #if COMMAND_LINE_SIZE > 256 BSSstart = ParmBSS Parmline = BSSstart + SECTOR_SIZE #else Parmline = ParmBSS BSSstart = Parmline + CL_LENGTH #endif !************************************ ! BSS data: ! moved from volume.S #define BSS_DATA .org BSSstart #include "volume.S" #ifdef SHS_PASSWORDS #include "shs3.S" #endif .align 512 BSSend = * BSSsize = BSSend-BSSstart Dataend = Parmline + SECTOR_SIZE .org max_secondary #if 0 DESCR0 = DESCR+2 #else DESCR0 = DESCR #endif