;  chain.S  -  LILO boot chainer 
;
; Copyright 1992-1998 Werner Almesberger.
; Copyright 1999-2003 John Coffman.
; All rights reserved.
;
; Licensed under the terms contained in the file 'COPYING' in the 
; source directory.
;

#define LILO_ASM
#include "lilo.h"
get common.s		/* as86 "include" will bypass the CPP */

; for debugging, set the EBDA size in Kilobytes; e.g., 64
#define EBDA 0

#define REVERSE_DL 1

#if defined(LCF_SOLO_CHAIN) && !defined(DOS_D)

#ifndef DOS_D
#define DOS_D
#endif

#ifndef CHECK
#define CHECK
#endif

#endif		/* LCF_SOLO_CHAIN */


#if VERSION_MINOR >= 50
#define DEBUG_NEW
#endif

	.text

	.globl	_main
	.org	0

_main:	jmp	start

	
	.org	6

	.ascii	"LILO"
	.word	STAGE_CHAIN
	.word	VERSION

offset:	.word	0
drive:	.byte	0			! drive, 0x80, 0x81
	.byte	0			! head, always zero

hint:	.word	drvmap			! pointer to drive map

ptable:	.blkw	0x20			! partition table to preload

cmd:	.word	0,0			! command line to pass on

; ES:DI contains a pointer to the command line passed from second-stage

start:
	xor	bx,bx			! set SS:SP to 0:7C00
	mov	ss,bx
	mov	sp,#BOOTSEG*16		! #0x7C00
	mov	bp,sp			! address from BP
#if EBDA
	push	#0x40
	pop	ds
	mov	word ptr [0x13],#640-EBDA	; simulate EBDA in Kilobytes
#endif
	push	cs
	pop	ds

#ifdef DEBUG_NEW
	push	bx
	push	bp

	push	es
	pop	ds
	mov	si,di
	call	say

	push	cs
	pop	ds
	mov	si,#crlf
	call	say

	call	bd1
	.ascii	"Boot drive 0x"
	.byte	0
bd1:	pop	si
	call	say
	mov	al,drive
	call	bout
	mov	si,#crlf
	call	say

	pop	bp
	pop	bx
#endif

; process the two 0xFFFF drive-map records for "master-boot"

	cmp	WORD [drvmap],#-1	! test for "master-boot"
	jne	noswap
	mov	al,drive		! boot drive
	mov	ah,al
	and	al,#0x80		! AL is master drive 0 or 80
	cmp	ah,al			! test for no mapping
	je	remswap

	mov	drvmap,ax		! 80 -> boot
	xchg	ah,al
	mov	drvmap+2,ax		! boot -> 80
	jmp	noswap

remswap:
	mov	si,#drvmap+4		! skip two words
rs2:	lodsw
	mov	(si-6),ax		! SI has been incremented
	or	ax,ax
	jnz	rs2

noswap:

	mov	al,#0x3D		! '=' sign
	mov	cx,#-1
	repne
	  scasb				! scan for =
srch:	seg es
	  mov	al,(di)
	inc	di
	cmp	al,#0x20		! test for space
	ja	srch
; real command line if AL==space, no command line if NUL
	or	al,al
	jz	nocmd
	mov	[cmd],di
	mov	[cmd+2],es
nocmd:

	mov	es,bx
	mov	cx,#SECTOR_SIZE/2
mtmp = SETUPSECS-1			! broken math ...
	mov	si,#mtmp*SECTOR_SIZE
	mov	di,bp			! #0x7C00
	rep
	  movsw
#ifdef DOS_D
#ifdef CHECK
	mov	si,#BOOTSEG*16+0x24	; address of first byte to test

	cmp	byte (bp+0x15),#0xf8		; check media descriptor
	jne	ck_failed

	seg	es
	  lodsb
	cmp	al,#0x80		; check range of device codes
	jb	ck_failed
	cmp	al,#0x8f
	ja	ck_failed

	seg	es
	  lodsb
	or	al,al			; check hi-byte is empty
	jnz	ck_failed

	seg	es
	  lodsb
	cmp	al,#0x29		; I do not know what this byte means
	je	ck_okay
	cmp	al,#0x28		; HPFS marker
	jne	ck_failed

ck_okay:
	lea	si,(si+4)		; address of vol label & fs type
	mov	cx,#11			; volume label (11)
ck_next:
	seg	es
	  lodsb
	or	al,al
	js	ck_failed		; not alphabetic if >= 0x80
	jz	ck_loop			; NUL allowed for HPFS
	cmp	al,#0x20
	jb	ck_failed		; not alphabetic if < SPACE
ck_loop:
	loop	ck_next

	mov	cx,#8			; check Filesystem type
ck_fstype:
	seg	es
	  lodsb
	or	al,al			; not alphabetic if >= 0x80
	js	ck_failed
	cmp	al,#0x20		; not alphabetic if < SPACE
	jb	ck_failed
	loop	ck_fstype

#endif
dos4:

	call	revmap1

	mov	(bp+0x24),dx		! fill in 0x24 and 0x25
	mov	si,offset
#ifdef LCF_M386
	mov	edx,ptable+8(si)
	mov	(bp+0x1C),edx
#else
	mov	dx,ptable+8(si)
	seg	es
	  mov	BOOTSEG*16+0x1C,dx
	mov	dx,ptable+10(si)
	seg	es
	  mov     BOOTSEG*16+0x1E,dx
#endif

#ifdef DEBUG_NEW
	mov	si,#update
	jmp	ck_say   
ck_failed:
	mov	si,#no_update
ck_say:
	call	say
#else
ck_failed:
#endif

#endif
	mov	cx,#0x20		! move partition table
	mov	si,#ptable
	mov	di,#PART_TABLE
	rep
	movsw
					! mess with the partition table
#if defined(LCF_REWRITE_TABLE) && !defined(LCF_READONLY)
	mov	si,#prtmap		! get partition table change rules
prtclp:	lodsw				! bios == 0 indicates end
	or	al,al
	jz	pmend			! at end -> quit
	cmp	al,cache		! already in cache ?
	je	incache			! yes -> no loading required
	push	ax			! save table data
	call	flush			! flush the cache
	pop	ax
	push	ax
	mov	cache,al		! remember drive in cache
#if 0
	cmp	al,drive		! boot drive ?
#else
	call	revmap1
	cmp	al,dl
#endif
	jne	noc			! no -> load into scratch area
	xor	ax,ax			! load at 0000:0600
	mov	bx,#PARTS_LOAD
	jmp	loadit

pmend:	call	flush			! flush table
	br	nopp			! and proceed

noc:	mov	ax,ds
	mov	bx,#PARTS_SCR		! scratch area	0000:0800
loadit:	mov	es,ax			! set up pointers and remember them
	mov	ces,ax
	mov	cbx,bx
	mov	ax,#0x201		! load partition table, one sector
	mov	dx,cache		! drive from cache (DH = 0)
	mov	cx,#1
#ifdef DEBUG_NEW
	pusha
	mov	al,dl			! dump device code
	call	bout
	mov	si,#msg_load		! say loading
	call	say
	popa
#endif
	int	0x13			! load it
	jc	wrfail			! error -> abort
	pop	ax			! get BIOS and offset
incache:les	bx,cbx			! load pointer
	add	bx,#PART_TABLE_OFFSET	! move to partition table
	add	bl,ah			! offset is always in [0x1be,0x1fd]
	lodsw				! see what we need to do
	seg	es			! match ?
	cmp	byte ptr (bx),al
	jne	nocng			! no -> do not change
	seg	es			! change
	mov	byte ptr (bx),ah
	mov	byte ptr dirty,#1	! mark as dirty
nocng:	br	prtclp			! next one

flush:	test	byte ptr dirty,#1	! dirty ?
	jz	noflush			! no -> do not write
	mov	ax,#0x301		! write one sector
	mov	dx,cache		! get the drive
	or	dl,dl			! nothing cached ?
	jz	noflush			! no -> do not flush
	les	bx,cbx			! reload pointer
#ifdef DEBUG_NEW
	pusha
	mov	al,dl			! dump device code
	call	bout
	mov	si,#msg_write		! say writing
	call	say
	popa
#endif
	int	0x13			! write ...
	jc	wrfail			! argl
noflush:ret
wrfail:	mov	si,#failmsg		! complain
	call	say
	mov	ax,#FIRSTSEG		! try to restart LILO
	jmpi	#GO,FIRSTSEG

cache:	.byte	0			! drive, 0 means not cached
	.byte	0			! head, always 0
cbx:	.blkw	1
ces:	.blkw	1
dirty:	.byte	0

#endif

; reverse drive mapping
;	uses AX
;       updates DL
;
revmap1:
	push	si
	mov	dx,drive	; get drive/head pair
	mov	si,#drvmap
rev0:	lodsw			; get to, from pair
	or	ax,ax		; test for end
	jz	rev9		; done
	cmp	ah,dl		; booting from "to"
	jne	rev0		; loop if not
	mov	dl,al		; substitute the "from"
rev9:	pop	si		; restore SI
	ret

nopp:
	mov	ax,drvmap		! need to install mapper ?
	or	ax,ax
	jz	noimap			! no -> go on
	call	swap13
noimap:

#if REVERSE_DL
	call	revmap1
#else
	mov	dx,drive		! initialize DX (drive and head)
#endif
	mov	si,offset		! DS:SI and ES:SI point to the partition
	add	si,#PART_TABLE
#ifdef DEBUG_NEW
	pusha

	mov	cx,# 4<<4		; delay 4 seconds
	xor	dx,dx
	mov	ah,# 0x86
	int	0x15			! Delay 6 seconds
#ifdef DEBUG_CONTINUE
	jnc	delayed

	mov	si,#msg_cont		! Hit any key ...
	call	say
	xor	ax,ax
	int	0x16	! AH==0, get key
delayed:
#endif

	popa
#endif

	xor	ax,ax			! set DS and ES to zero
	mov	ds,ax
	mov	es,ax
	mov	bx,#BOOTSEG*16
	
	mov	ss,ax			! on all processors since the 186
	mov	sp,bx			! these instructions are locked
	
#ifdef LCF_COHERENT
	mov	(si),dl			! yes, put it in the partition table
#endif
	mov	bp,si			! BP==SI flags hard disk boot
	push	ax
	push	bx
	seg ss
	cmp	byte ptr (bx+par1_cli),#0xFA	! first.S starts with CLI
	je	try_sig
	cmp	byte ptr (bx+par1_cli),#0xEB	! short jump?
	jne	gotoit
	push	ax
	mov	al,(bx+par1_cli+1)		! get offset
	cbw
	inc	ax
	inc	ax
	add	bx,ax			! test relocated record
	pop	ax
	cmp	byte ptr (bx+par1_cli),#0xFA	! first.S starts with CLI
	jne	gotoit			! not LILO if no CLI
try_sig:
#ifdef LCF_M386
	seg ss
	  cmp	dword ptr (bx+par1_signature),#EX_MAG_HL
#else
	seg ss
	  cmp	word ptr (bx+par1_signature),#EX_MAG_L
	jne	gotoit
	seg ss
	  cmp	word ptr (bx+par1_signature+2),#EX_MAG_H
#endif
	jne	gotoit		! LILO signature required for command line
#ifdef LCF_M386
	seg cs
	  cmp	dword ptr [cmd],#0
#else
	push	ax
	seg cs
	  mov	ax,[cmd]
	seg cs
	  or	ax,[cmd+2]
	pop	ax
#endif
	je	gotoit

;  pass on a command line
	seg cs
	  les	bx,[cmd]
	lea	si,(bx-5)
#ifdef LCF_M386
	seg es
	  mov	dword ptr (si),#EX_MAG_HL
#else
	seg es
	  mov	word ptr (si),#EX_MAG_L
	seg es
	  mov	word ptr (si+2),#EX_MAG_H
#endif
	mov	dl,#EX_DL_MAG
gotoit:
	retf


#ifdef XXX
lilosig:.ascii	"LILO"
cmd:	.ascii	"98"
	.byte	0
#endif

#if defined(LCF_REWRITE_TABLE) || defined(DEBUG_NEW)

! Display a NUL-terminated string on the console

say:	push	ax
	push	bx		! save BX
say_2:	lodsb			! get byte
	or	al,al		! NUL ?
	jz	aret		! yes -> done
	mov	ah,#14		! display, tty-style
	mov	bx,#0007
	int	0x10
	jmp	say_2		! next one
aret:	pop	bx		! restore
	pop	ax
	ret			! done

failmsg:.ascii	"Rewrite error."
	.byte	13,10,0

#endif

#ifdef DEBUG_NEW
wout:	push	ax
	xchg	ah,al
	call	bout		! write hi-byte
	pop	ax
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]

	push	bx
	mov	ah,#14		! display, tty-style
	xor	bh,bh
	int	0x10
	pop	bx
	ret

msg_swap13:
	.ascii	"Install drive Swapper"
	.byte	13,10,0
msg_load:
	.ascii	" - PT loaded"
	.byte	13,10,0
msg_write:
	.ascii	" - PT written"
	.byte	13,10,0
no_update:
	.ascii	"NO "
update:
	.ascii	"24-25 update has occurred"
crlf:	.byte	13,10,0

#ifdef DEBUG_CONTINUE
msg_cont: .ascii  "\r\nHit any key to continue ..."
	.byte	0
#endif

#endif	/* DEBUG_NEW */

swap13: 
#ifdef DEBUG_NEW
	mov	bx,#drvmap
sw13b:	mov	ax,(bx)		; get drvmap entry
	or	ax,ax
	jz	sw13z
	call	bout
	call	sw13a
	.ascii	" -> "
	.byte	0
sw13a:	pop	si
	inc	bx
	call	say
	mov	al,(bx)
	call	bout
	inc	bx
	mov	si,#crlf
	call	say
	jmp	sw13b
sw13z:
	mov	si,#msg_swap13
	call	say
#endif
	seg	es		! allocate 1 kB
	dec	word ptr [0x413]
	int	0x12		! get start segment
	shl	ax,#6		! we are running on a 386
	cli			! disable interrupts
	xor	bx,bx		! zero a few registers
	mov	di,bx
	seg	es		! change offset
	xchg	bx,[4*0x13]	! 0x4c
	mov	old13of,bx
	mov	bx,ax		! change segment
	seg	es
	xchg	bx,[4*0x13+2]	! 0x4e
	mov	old13sg,bx
	mov	es,ax		! move drive swapper
	mov	si,#new13
	mov	cx,#new13end-new13
	rep
	movsb
	sti			! enable interrupts
	ret			! done

	.even
new13:	push	ax		! save AX (contains function code in AH)
	push	bp		! need BP to mess with stack
	mov	bp,sp
	jmp	new13a		! make space for signature

	.org	new13+6
	.ascii	"LILO"
	.word	STAGE_DRIVE_MAP
	.word	VERSION
	.word	drvmap-new13	! relative pointer to drive map

new13a:
	! Stack layout:
	!
	!   +8	INT flags
	!   +6	INT CS
	!   +4	INT IP
	!   +2	AX
	! BP+0 BP
	pushf			! push flags (to act like interrupt)
	push	si
	mov	si,#drvmap-new13
mapfl:	seg	cs		! get next entry
	mov	ax,(si) 	! do not depend on DIRECTION flag
	lea	si,(si+2)	! **
	or	ax,ax		! at end ?
	jz	nomap		! yes -> do not map
	cmp	dl,al		! match ?
	jne	mapfl		! no -> continue
	mov	dl,ah		! map drive
nomap:	pop	si		! restore SI
	mov	8(bp),ax	! overwrite old flags (to remember mapping)
	mov	ax,2(bp)	! restore AX
	mov	bp,(bp)		! restore BP
	.byte	0x9a		! CALL FAR
old13of:.word	0
old13sg:.word	0
	push	bp		! save BP again
	mov	bp,sp
	! New stack layout:
	!
	!   +10	mapping (was flags)
	!   +8	INT CS
	!   +6	INT IP
	!   +4	AX
	!   +2  obsolete BP
	! BP+0  BP
	xchg	ax,4(bp)	! save AX and get command
	pushf			! fix driver number, if necessary
	cmp	ah,#8 ! do not fix
	je	done13
	cmp	ah,#0x15 ! do not fix
	je	done13
	mov	ax,10(bp)	! no mapping ?
	or	ax,ax
	jz	done13
	mov	dl,al		! fix mapping
done13:	mov	ax,4(bp)	! restore AX
	pop	10(bp)		! restore flags
	pop	bp		! get BP
	add	sp,#4		! fix SP
	iret			! done
	
	.even
drvmap:	.blkw	DRVMAP_SIZE+1

new13end:

#if defined(LCF_REWRITE_TABLE)
prtmap:	.blkw	PRTMAP_SIZE*2+1	! only first word of last entry is read
#endif

theend:
