/* second.S  -  LILO second stage boot loader */

/* Written 1992-1994 by Werner Almesberger */


#include <linux/config.h>

#define LILO_ASM
#include "lilo.h"

#ifdef DEBUG
#define DBG_LREAD 1
#endif

#define UI_MAGIC 0xff		/* take input from keyboard */


	.text

	.globl	_main
	.org	0

_main:	jmp	start

	.org	2

! Boot device parameters. They are set by the installer.

sig:	.ascii	"LILO"
stage:	.word	STAGE_SECOND
version:.word	VERSION

	.org	CODE_START_2

start:	seg	ss		! use a COM port ?
	mov	dx,DSC_OFF-6+SSDIFF
	sub	dl,#1
	jc	nocom		! no -> go on
	xor	ax,ax		! initialize the serial port
	xchg	al,dh
	push	dx
	int	0x14
	xor	ax,ax		! get the port address
	mov	ds,ax
	pop	bx
	shl	bx,1
	mov	dx,(bx+0x400)
	seg	cs		! keep it
	mov	slbase,dx
	mov	cx,#32		! drain the queue (if any)
drain:	in	al,dx
	loop	drain
	add	dx,5		! clear the status register
	in	al,dx
	mov	al,#13		! send "\r\nLI" to the serial port
	call	serdisp
	mov	al,#10
	call	serdisp
	mov	al,#0x4c
	call	serdisp
	mov	al,#0x49
	call	serdisp

nocom:	mov	al,#0x4c	! display an 'L'
	call	display
	xor	ax,ax		! get pointer to disk parameter table in DS:SI
	mov	ds,ax
	lds	si,0x78
	cmp	byte ptr (si+4),#9 ! okay ?
	ja	dskok		! yes -> don not patch
	mov	ax,cs		! get pointer to new area in ES:DI
	mov	es,ax
	mov	di,#dskprm
	mov	cx,#6		! copy 12 bytes
	rep
	movsw
	seg	es		! patch number of sectors
	mov	byte ptr (di-8),#18
	cli			! paranoia
	xor	ax,ax		! store new pointer
	mov	ds,ax
	mov	0x78,#dskprm
	mov	0x7a,es
	sti
dskok:	seg	cs		! clear the break flag
	mov	byte ptr break,#0
	call	instto		! get timer interrupt
	jmp	restrt		! get going

! Die

crshbrn:mov	al,#63		! display a question mark
	call	display
zzz:	jmp	zzz		! sit here forever

! Restart here after a boot error

restrt: mov	ax,cs		! adjust segment registers
	mov	ds,ax
	mov	es,ax
	cli			! reset SP (we might have restarted with some
	mov	sp,#STACK	! data on the stack)
	sti
	cmp	sig,#0x494c	! check that this is really loaded at the
				! right place ...
	jne	crshbrn
	cmp	sig+2,#0x4f4c
	jne	crshbrn
	cmp	stage,#STAGE_SECOND
	jne	crshbrn
	cmp	version,#VERSION
	jne	crshbrn
	mov	cmdbeg,#acmdbeg	! probably unattended boot
ldsc:	mov	al,#1		! load the descriptor table
	mov	bx,#DESCR
	seg	ss
	mov	cx,DSC_OFF+SSDIFF
	seg	ss
	mov	dx,DSC_OFF+2+SSDIFF
	call	cread
	jc	fdnok		! error -> retry
	mov	al,#1		! load the second sector
	mov	bx,#DESCR+512
	seg	ss
	mov	cx,DSC_OFF2+SSDIFF
	seg	ss
	mov	dx,DSC_OFF2+2+SSDIFF
	call	cread
	jc	fdnok		! error -> retry
	mov	si,#DESCR	! compute a checksum of the descriptor table
	mov	bx,#INIT_CKS
	cld
	mov	cx,#0x200
csloop:	lodsw
	xor	bx,ax
	loop	csloop
	or	bx,bx		! okay ?
	jnz	chkerr
	mov	al,#1		! load the default command line
	mov	bx,#DFLCMD
	seg	ss
	mov	cx,DFCMD_OFF+SSDIFF
	seg	ss
	mov	dx,DFCMD_OFF+2+SSDIFF
	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),#0 ! erase the magic number
	mov	al,#1
	seg	ss
	mov	cx,DFCMD_OFF+SSDIFF
	seg	ss
	mov	dx,DFCMD_OFF+2+SSDIFF
	call	cwrite
#endif
	jmp	dokay		! ignore errors
bdcmag:	mov	byte ptr (bx+2),#0 ! disable the command line
	jmp	dokay		! go on
fdnok:	xor	ax,ax		! reset FDC
	mov	dl,al
	int	0x13
	jmp	ldsc		! retry

! Checksum error

chkerr:	mov	al,#45		! display a minus sign
	call	display
sixfeet:jmp	sixfeet		! sit here forever

! List all known boot images

list:	mov	byte ptr (bx),#0 ! set EOL marker
	mov	bx,#crlf	! display a CRLF
	call	say
	mov	si,#DESCR+2	! 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
	inc	dl		! count the image
	test	dl,#3		! inside line -> go on
	jnz	fill
	mov	bx,#crlf	! new line
	call	say
	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,#DESCR_SIZE-MAX_IMAGE_NAME
	loop	lloop		! next image
ldone:	test	dl,#3		! already at BOL ?
	jz	atbol		! yes -> no CRLF
	mov	bx,#crlf	! display a CRLF
	call	say
atbol:	br	iloop		! done

! Ready to process user input

dokay:	mov	bx,#ospc	! display 'O '
	call	say
	mov	ospc,#0		! disable the message
	mov	word ptr vgaovr,#VGA_NOCOVR ! disable VGA override
	xor	ax,ax		! get the delay and disable further delays
	seg	ss
	xchg	ax,DSC_OFF-8+SSDIFF
#ifdef LCF_ONE_SHOT
	or	old_to,ax
#endif
	mov	nodfl,#iloop	! interactive prompt if falling through
	cmp	ax,#0xffff	! enter boot prompt ?
	je	extp		! yes -> check for external parameters
	mov	nodfl,#bfirst	! boot first image if falling through
	call	setto		! set the timeout

! Shift wait loop

actlp:	mov	ah,#2		! get shift keys
	int	0x16
	and	al,#0x5f	! anything set ? (except NumLock)
	jnz	iloop		! yes -> enter interactive mode
	mov	dx,slbase	! using a serial port ?
	or	dx,dx
	jz	acnosp		! no -> go on
	cmp	byte ptr break,#0 ! break received ?
	jnz	iloop		! yes -> enter interactive mode
	add	dx,#5		! check for pending break
	in	al,dx
	test	al,#0x10
	jnz	iloop		! break received -> enter interactive mode
acnosp:	test	byte ptr timeout,#1 ! timed out ?
	jz	actlp		! no -> wait

! Check for external parameters

extp:	seg	ss		! external parameters ?
	cmp	byte ptr EX_OFF+6,#EX_DL_MAG
	jne	noex		! no -> go on
	seg	ss		! clear flag
	mov	byte ptr EX_OFF+6,#0
	seg	ss		! load the signature pointer
	les	bx,EX_OFF
	seg	es		! "LI" ?
	cmp	(bx),#EX_MAG_L
	jne	noex		! no -> go on
	seg	es		! "LO" ?
	cmp	(bx+2),#EX_MAG_H
	jne	noex		! no -> go on
	seg	ss
	mov	si,EX_OFF+4	! pointer to the command line
	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:	mov	ax,cs		! restore ES
	mov	es,ax
	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

tolist:	br	list		! ...

! Command input processor

iloop:	seg	ss		! message disabled ?
	cmp	MSG_OFF+SSDIFF,#0
	je	nomsg		! yes -> skip this
	mov	bx,#crlf	! move to a new line
	call	say
	seg	ss		! load the message file
	mov	cx,MSG_OFF+SSDIFF+2
	seg	ss
	mov	dx,MSG_OFF+SSDIFF+4
	mov	bx,#MAP
	mov	al,#1
	call	sread
	call	loadfile
	seg	ss
	mov	ax,#SYSSEG
	mov	ds,ax
	xor	bx,bx		! set the terminating NUL and disable further
				! messages
	seg	ss
	xchg	bx,MSG_OFF+SSDIFF
	mov	byte ptr (bx),#0
	xor	bx,bx		! display the message
	call	say
	mov	ax,cs		! restore segment registers
	mov	ds,ax
	mov	es,ax
nomsg:	mov	cmdbeg,#acmdbeg	! probably unattended boot
	mov	si,#usrinpm	! interactive mode
niloop:	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
kbinp:	mov	cx,#brto	! get a key
	call	getkey
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,#13		! CR ?
	je	cr		! yes -> go on
	cmp	al,#8		! BS ?
	je	todelch		! yes -> erase one character
	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:	mov	(bx),al		! store in the buffer
	inc	bx		! increment pointer
	push	bx
	call	display		! echo
	pop	bx
	cmp	bx,#cmdline+CL_LENGTH ! at end of buffer ?
	jb	input		! no -> next input

! End of input, process the command line

cr:	mov	cmdbeg,#mcmdbeg ! probably manual boot
nul:	mov	byte ptr break,#0 ! clear the break flag
	mov	ax,cs		! set ES to CS
	mov	es,ax
	xor	al,al		! mark end
	mov	(bx),al
	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="
	mov	di,si
	cld
chkvga:	cmp 	word ptr (si),#0x6776 ! "vga=" ?
	jne	vsknb		! no -> skip to next blank
	cmp	word ptr (si+2),#0x3d61
	jne	vsknb
	call	setvga		! set VGA mode
	jc	toiloop		! error -> get next command
	dec	di		! discard last blank
vsknb:	lodsb			! copy one byte
	stosb
	cmp	al,#32		! space ?
	je	chkvga		! yes -> look for "vga=" again
	or	al,al		! at end ?
	jnz	vsknb		! no -> go on
	mov	bx,#crlf	! display a CRLF
	call	say
	cmp	di,#cmdline+1	! empty line ?
emptyl:	je	bfirst		! yes -> boot first image
	jmp	bcmd		! boot the specified image

todelch:jmp	delch		! ...
toiloop:br	iloop		! ...
toinput:br	input		! ...
todell:	jmp	delline		! ...

! Find the boot image and start it

bcmd:	mov	cx,#IMAGES	! test all names
	mov	bx,#DESCR+2
nextn:	mov	si,bx		! compare the names
	mov	di,#cmdline
nextc:	mov	al,(si)		! get next character
	or	al,al		! NUL ?
	jz	dscend		! yes -> possible match
				! get the character
#ifdef LCF_IGNORECASE
	call	upcase
#endif
	mov	ah,al
	mov	al,(di)
#ifdef LCF_IGNORECASE
	call	upcase
#endif
	cmp	al,ah		! character equal ?
	jne	skipn		! no -> try next one
	inc	si		! test next character
	inc	di
	jmp	nextc
dscend:	cmp	byte ptr (di),#32 ! space or NUL -> boot
	je	boot
	cmp	byte ptr (di),#0
	je	boot
skipn:	add	bx,#DESCR_SIZE	! test next name
	loop	nextn
	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
	push	bx		! display BS,SPC,BS
	mov	bx,#bs
	call	say
	pop	bx
	dec	bx		! move the pointer
	jmp	toinput		! go on

! Delete the entire line

delline: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

! Boot first after timeout

brto:	mov	bx,#crlf	! display a CRLF
	call	say
	jmp	brfrst		! boot

! Boot the first image

bfirst:	cmp	byte ptr (cmdline),#0 ! is there a default ?
	jne	bcmd		! yes -> boot that image
brfrst:	mov	bx,#DESCR+2	! boot the first image
	mov	si,bx		! copy the name to the command line
	mov	di,#cmdline
	cld
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	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	(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
	cmp	byte ptr (bx+MAX_IMAGE_NAME+1),#0 ! use a password ?
	je	toboot		! no -> boot
	test	(bx+FLAGS_OFF),#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:	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:	mov	bx,#crlf	! display a CRLF
	call	say
	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
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
	add	si,#ADDR_OFFS
	cld
	lodsw			! address of the options sector
	mov	cx,ax
	lodsw
	mov	dx,ax
	inc	si		! skip number of sectors
	push	si		! save SI
#ifdef DEBUG
	mov bx,#step0
	call say
#endif
	mov	bx,#MAP		! load the options sector
	mov	al,#1
	call	sread
	mov	si,cmdbeg	! copy non-options part of command line
	mov	di,#PARMLINE
cpnocl:	cmp	si,options	! at beginning of options ?
	je	cpnodn		! yes -> go on
	movsb			! copy one byte
	jmp	cpnocl		! next one
cpnodn:	mov	si,#MAP		! constant options ?
	cmp	byte ptr (si),#0
	je	nocopt		! no -> go on
	mov	al,#32		! add a space
	stosb
cpcodsp: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
	cmp	al,#32		! a space ?
	je	cpcodsp		! yes -> discard next
	lodsb			! get next byte
	jmp	cpcolp
cpcodn:	cmp	byte ptr (di-1),#32 ! last was space ?
	jne	nocopt		! no -> go on
	dec	di		! discard it
nocopt:	mov	si,options	! append variable options
cpvalp:	lodsb			! copy one byte
	stosb
	or	al,al		! NUL ?
	jnz	cpvalp		! no -> go on
	pop	si
	lodsw			! address of the first map sector
	mov	cx,ax
	lodsw
	mov	dx,ax
	inc	si		! skip number of sectors
	push	si		! save SI
#ifdef DEBUG
	mov bx,#step1
	call say
#endif
	mov	bx,#MAP		! load the first map sector
	mov	al,#1
	call	sread
	mov	moff,#0		! reset the pointer
#ifdef DEBUG
	mov bx,#step2
	call say
#endif
	mov	ax,#INITSEG	! load the original boot sector
	mov	es,ax
	xor	bx,bx		! load now
	call	load1
	seg	es
	mov	CL_MAGIC_ADDR,#CL_MAGIC ! set magic number
	seg	es
	mov	word ptr CL_OFFSET,#PARMLINE+SECOND_SS
				! set parameter line offset
	pop	si		! restore SI
	lodsw			! get flags bit map
	mov	bx,ax
	lodsw			! copy parameters ... VGA mode ...
	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	506,ax
novga:
#ifdef DEBUG
	mov bx,#step3
	call say
#endif
	mov	ax,#SETUPSEG	! load the setup (might meet EOF in the
	mov	es,ax		! process if loading the boot sector of an
	xor	bx,bx		! other operating system)
	mov	cx,#SETUPSECS
lsetup:	push	cx
	call	loadopt
	pop	cx
	loop	lsetup
#ifdef DEBUG
	mov bx,#step4
	call say
#endif
	call	loadfile	! load the system
	jmp	launch		! go !

loadfile:mov	ax,#SYSSEG	! load a file at SYSSEG:0000
	mov	es,ax
	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
	jmpi	restrt,SECONDSEG
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	cx,MAP(si)	! get address
	mov	dx,MAP+2(si)
	mov	al,MAP+4(si)
	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,#5		! increment pointer
	mov	moff,si
	cmp	si,#508		! page end ?
	jb	todold		! no -> load it
	mov	moff,#0		! reset pointer
	push	cs		! adjust ES
	pop	es
	mov	bx,#MAP		! load map page
	call	sread
	mov	al,#0x2e	! print a dot
	call	display
	jmp	lfetch		! try again

todold:	br	doload		! ...

! Start the kernel

launch: push	es		! save ES:BX
	push	bx
	mov	bx,#crlf	! display a CRLF
	call	say
	mov	dx,#0x3f2	! stop the floppy motor
	xor	al,al
	outb
	xor	ax,ax		! reset the FDC
	mov	dl,al
	int	0x13
	call	remto		! free timer interrupt
	mov	ax,#INITSEG	! adjust segment registers
	mov	ds,ax
	mov	es,ax
	jmpi	0,SETUPSEG	! start the setup

! Load one sector (called from load)

doload:	pop	bx		! restore ES:BX
	pop	es

! Load a sequence of sectors

sread:	push	bx		! save registers
	push	cx
	push	dx
	mov	tempal,al	! save AL
	call	cread
	jc	rerror		! error -> complain
	pop	dx		! restore registers
	pop	cx
rokay:	pop	bx
	xor	ax,ax		! compute offset
	mov	ah,tempal
	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:
aret:	ret			! done

! Read error - try a second time and give up if that fails too

rerror:	xor	ax,ax		! reset the disk
	mov	dl,al
	int	0x13
	pop	dx		! try again
	pop	cx
	pop	bx
	push	bx
	mov	al,tempal
	call	cread
	jnc	rokay		! okay -> go on
	push	ax
	mov	bx,#msg_re	! say something
	call	say
	pop	ax		! display the error code
	mov	al,ah
	call	bout
	mov	bx,#crlf	! a CRLF
	call	say
	mov	moff,#0		! restore initial state
	jmpi	restrt,SECONDSEG

! 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

! Display a hexadecimal byte

#ifdef DEBUG
wout:	push	ax
	mov	al,ah
	call	bout
	pop	ax
#endif
bout:	push	ax		! save byte
	shr	al,#4		! display upper nibble
	call	nout
	pop	ax
nout:	and	al,#15		! lower nible only
	add	al,#48		! display lower nibble
	cmp	al,#58		! convert to ASCII
	jb	nokay
	add	al,#7
nokay:	br	display		! display it

! Display a NUL-terminated string on the console

say:	mov	al,(bx)		! get byte
	or	al,al		! NUL ?
	jz	aret		! yes -> done
	push	bx		! save pointer
	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
	mov	ah,#0xf		! clear the local screen
	int	0x10
	xor	ah,ah
	int	0x10
	jmp	snext		! next character
nocls:	call	display		! display, tty-style
snext:	pop	bx
	inc	bx		! next one
	jmp	say

! Get a key (CX = timeout exit)

getkey: seg	ss		! set the timeout
	mov	ax,DSC_OFF-10+SSDIFF
	call	setto
gwtkey:	mov	ah,#1		! is a key pressed ?
	int	0x16
	jnz	gotkey		! yes -> get it
	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
gnokey:	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
gotch:
#ifdef LCF_ONE_SHOT
	cmp	old_to,#0xffff	! one-shot timeout ?
	jne	noosht		! no -> go on
	seg	ss		! disable timeout
	mov	DSC_OFF-10+SSDIFF,#0xffff
noosht:
#endif
	ret			! done

! Timeout handling

instto:	push	ds		! install the timeout handler
	xor	ax,ax
	mov	ds,ax
	cli			! no interrupts
	mov	ax,[0x1c*4]	! get the old vector
	seg	cs
	mov	int1c_l,ax
	mov	ax,[0x1c*4+2]
	seg	cs
	mov	int1c_h,ax
	mov	[0x1c*4],#tick	! install new vector
	mov	[0x1c*4+2],cs
	sti			! done
	pop	ds
	ret

remto:	push	es		! remove the interrupt handler
	xor	ax,ax
	mov	es,ax
	cli
	mov	ax,int1c_l	! restore the old vector
	seg	es
	mov	[0x1c*4],ax
	mov	ax,int1c_h
	seg	es
	mov	[0x1c*4+2],ax
	sti			! done
	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	cntdown,#0xffff
	je	notzro		! yes -> go on
	seg	cs		! decrement counter
	dec	cntdown
	jnz	notzro		! not zero -> go on
	seg	cs		! set timeout flag
	mov	byte ptr timeout,#0xff
notzro:	popf			! done
	seg	cs
	jmpi	(int1c_l)	! continue with old interrupt

! Sector write

cwrite:	mov	byte ptr (dsk_cmd),#3 ! set command to write
	call	cread		! do it
	mov 	byte ptr (dsk_cmd),#2 ! read again
	ret			! done

! Sector read

#define SECOND_STAGE
#include "shared.S"

! 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
	add	bx,#2		! compare the strings
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:	mov	vgaovr,cx	! set VGA mode
	dec	si		! read last character again
	clc			! okay, done
	ret
vganum:	pop	si		! get SI
	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 ?
	jb	vgaerr		! no -> error
	cmp	al,#57
	ja	vgaerr
	sub	al,#48		! cx = cx*10+al-'0'
	mov	bx,cx
	shl	cx,1
	shl	cx,1
	add	cx,bx
	shl	cx,1
	add	cx,ax
	jnc	vgadig		! next one
vgaerr:	mov	bx,#msg_v	! display an error message
	call	say
	stc			! return an error
	ret

vgatab:	.word	0xfffd
	.ascii	"ASK"
	.byte	0
	.word	0xfffe
	.ascii	"EXTENDED"
	.byte	0
	.word	0xfffe
	.ascii	"EXT"
	.byte	0
	.word	0xffff
	.ascii	"NORMAL"
	.byte	0
	.word	0

! Some messages

msg_p:	.ascii	"boot: "
	.byte	0

msg_l:	.ascii	"Loading "
	.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_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

ospc:	.ascii	"O"
	.byte	32,0

crlf:	.byte	10,0

bs:	.byte	8,32,8,0

#ifdef DEBUG
step0:	.ascii	" options,"
	.byte	0
step1:	.ascii	" map page,"
	.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
sdx:	.ascii	" DX="
	.byte	0
ses:	.ascii	" ES="
	.byte	0
sdone:	.byte	10
	.byte	0
#endif

tempal:	.byte	0
moff:	.word	0		! map offset

cntdown:.word	0		! count-down
timeout:.byte	0		! timed out

int1c_l:.word	0		! old timer interrupt
int1c_h:.word	0

#ifdef LCF_ONE_SHOT
old_to:	.word	0
#endif

nodfl:	.word	0		! action if no defaults are present

slbase:	.word	0		! serial port base (or 0 if unused)
break:	.byte	0		! break received flag

usrinpm:.byte	UI_MAGIC

cmdbeg:	.word	0
options:.word	0

vgaovr:	.word	0

dskprm:	.word	0,0,0,0,0,0

acmdbeg:.ascii	"auto "
mcmdbeg:.ascii	"BOOT_IMAGE"
prechr:	.byte	32		! space: guard double blank supression
				! equal sign: variable assignment
cmdline:.byte	0
