
;-----------------------------------------------------------------------------
; ElTorito.asm
;
; El Torito Bootable CD-ROM driver which does not reset the CD-ROM drive upon
; loading, but instead accesses the drive through BIOS system calls
;
; MIT License
;
; (c) 2000 by Gary Tong
; (c) 2001-2009 by Bart Lagerweij
;
; Permission is hereby granted, free of charge, to any person obtaining a copy
; of this software and associated documentation files (the "Software"), to deal
; in the Software without restriction, including without limitation the rights
; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
; copies of the Software, and to permit persons to whom the Software is
; furnished to do so, subject to the following conditions:
;
; The above copyright notice and this permission notice shall be included in
; all copies or substantial portions of the Software.
;
; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
; THE SOFTWARE.
;
;-----------------------------------------------------------------------------

; To assemble and link, use these commands with NASM 2.x:
;   nasm -Ox -f bin -o eltorito.sys eltorito.asm

; To enable Trace markers uncomment the line below
; DEBUG_TRACERS=1

; To enable debug info uncomment the line below
; DEBUG=1

%ifdef DEBUG_TRACERS
 %macro	TRACER	1
	call debug_tracer
	db %1
 %endmacro
%else
 %macro	TRACER	1
 %endmacro
%endif	; DEBUG_TRACERS

%define	Ver	'1.5'
%define CR	0DH, 0Ah
RPolyH		equ	0EDB8h
RPolyL		equ	08320h

		section .text align=16
		org	0

;=============================================================================

Cdrom:

NextDriver	dd	-1			;-+
Attributes	dw	0C800h			; |
Pointers	dw	Strategy		; |
		dw	Commands		; |   MSCDEX requires this
DeviceName	db	'ELTORITO'		; |  data in these locations
		dw	0			; |
DriveLetter	db	0			; |
NumUnitsSupp	db	1			;-+

DriverName	db	'El-Torito CD-ROM Device Driver',0
		align 4, db 0
ReqHdrLoc	dd	0
XferAddr	dd	0
Checksum	dd	-1
DriveNumber	db	0
ReadBytes	db	0			;0 --> 2048 bytes/sector
						;1 --> 1024 bytes/sector
						;2 -->  512 bytes/sector

Routines	dw	Init		;Init		;0
		dw	Unsupported	;MediaCheck	;1
		dw	Unsupported	;BuildBPB	;2
		dw	IoctlInput	;IoctlInput	;3
		dw	Unsupported	;Input		;4
		dw	Unsupported	;NonDesInput	;5
		dw	Unsupported	;InputStatus	;6
		dw	Unsupported	;InputFlush	;7
		dw	Unsupported	;Output		;8
		dw	Unsupported	;OutputVerify	;9
		dw	Unsupported	;OutputStatus	;10
		dw	Unsupported	;OutputFlush	;11
		dw	IoctlOutput	;IoctlOutput	;12
		dw	DoNothing	;DeviceOpen	;13
		dw	DoNothing	;DeviceClose	;14
		dw	ReadL		;ReadL		;128

IoctlICtrl	dw	Raddr		;Raddr		;0
		dw	Unsupported	;LocHead	;1
		dw	Unsupported	;(Reserved)	;2
		dw	Unsupported	;ErrStat	;3
		dw	Unsupported	;AudInfo	;4
		dw	DrvBytes		;DrvBytes	;5
		dw	DevStat		;DevStat	;6
		dw	SectSize		;SectSize	;7
		dw	VolSize		;VolSize	;8
		dw	MedChng		;MedChng	;9

SpecPkt		times	19	db	0	; offset 77h in 1.4
		times	13	db	0	; unknown extra 00s in 1.4

Greeting	db	'El-Torito Bootable CD-ROM Driver for Dos v',Ver,', http://www.nu2.nu/eltorito/',CR
		db	'  (c) 2000 by Gary Tong',CR
		db	'  (c) 2001-2002 by Bart Lagerweij',CR,0
DblSpace	db	'  ',0

;=============================================================================

Strategy:

		mov	word [cs:ReqHdrLoc],bx
		mov	word [cs:ReqHdrLoc+2],es
		retf


;=============================================================================

Commands:

		push	ax
		push	bx
		push	cx
		push	dx
		push	si
		push	di
		push	bp
;		pushad
		push	ds
		push	es
		TRACER 'C'

		cld				;Clear direction
		sti				;Enable interrupts

		mov	ax, cs			;ds=cs
		mov	ds, ax

		les	bx,[ReqHdrLoc]	;seg:offset ptr into es:bx
		xor	ax,ax
		mov	al,[es:bx+2]		;Get Command code
%ifdef DEBUG
		call	print_hex8
%endif
		cmp	al,15
		jb	Mult2			;If 0-14
		cmp	al,128
		jb 	UnknownCmd		;If 15-127
		cmp	al,129
		jb	ShiftDown		;If 128
UnknownCmd:	mov	al,121			;8 = Unsupported (Reserved)
ShiftDown:	sub	al,113			;128 --> 15, 121 --> 8
Mult2:		shl	al,1			;Convert into offset (*2)
		mov	di,Routines
		add	di,ax
		call 	word [di]		;Execute desired command
		or	ax,100h			;Set Return Status's Done bit
		lds	bx,[ReqHdrLoc]		;seg:offset ptr into ds:bx
		mov	[bx+3],ax		;Save Status

%ifdef DEBUG
		cmp	byte [cs:buffer+2048], 96h
		je	buffer_ok
		mov	al, '!'
		call	print_char
		jmp	$
buffer_ok:
%endif

		TRACER 'c'
		pop	es
		pop	ds
;		popad
		pop	bp
		pop	di
		pop	si
		pop	dx
		pop	cx
		pop	bx
		pop	ax
		retf


;=============================================================================

Unsupported:			;Unsupported Command

		mov	ax,8003h		;Set Status Error bit,
		TRACER 'U'
		TRACER 'C'
		retn				;   Error 3 = Unknown Command


;=============================================================================

IoctlInput:			;IOCTL Input Routine

		mov	di,[es:bx+14]		;es:bx --> Request Header
		mov	es,[es:bx+16]		;Get Xfer Address into es:di
		xor	ax,ax			;Get Control Block Code
		mov	al,[es:di]
%ifdef DEBUG
	TRACER 'I'
	TRACER 'O'
	call	print_hex8
%endif
		cmp	al,10
		jb	UnkIoctlI		;If 0-9
		mov	al,2			;Map to Unsupported
UnkIoctlI:	shl	al,1			;Convert into offset (*2)
		mov	si,IoctlICtrl
		add	si,ax
		call 	word [si]		;Execute desired command
		retn


;=============================================================================

Raddr:			;Return Device Header Address

		TRACER 'A'
		mov	word [es:di+1],0
		mov	[es:di+3],cs
		xor	ax, ax			;Set Return Status = success
		TRACER 'a'
		retn


;=============================================================================

DrvBytes:			;Read Drive Bytes

		TRACER 'B'
		push	di			;Save original Xfer Addr
		add	di,2			;Point to 1st dest byte
		mov	si,Greeting	;Point to Greeting
DrvB:		movsb				;Copy over a byte
		cmp	byte [si],13	;Is next char a CR?
		jne	DrvB			;Loop if not

		sub	di,2			;Get #bytes copied into ax
		mov	ax,di
		pop	di			;Retrieve original Xfer Addr
		sub	ax,di
		mov	byte [es:di+1],al	;and save it
		mov	ax,0			;Set Return Status = success
		TRACER 'b'
		retn


;=============================================================================

DevStat:			;Return Device Status

		TRACER 'D'
		mov	word [es:di+1],202h	;Door closed
		mov	word [es:di+3],0	;Door unlocked
						;Supports only cooked reading
						;Read only
						;Data read only
						;No interleaving
						;No prefetching
						;No audio channel manipulation
						;Supports both HSG and Redbook
						;  addressing modes

		xor	ax, ax			;Set Return Status = success
		TRACER 'd'
		retn


;=============================================================================

SectSize:			;Return Sector Size

		TRACER 'S'
		mov	word [es:di+2],2048
		mov	ax,0			;Set Return Status = success
		TRACER 's'
		retn


;=============================================================================

VolSize:			;Return Volume Size

		TRACER 'V'
		call	PriVolDesc		;Get and Check Primary Volume
						;  Descriptor
		mov	ax,800Fh		;Assume Invalid Disk Change
		jc	VolExit			;If Read Failure

		mov	ax,word [Buffer+80]	;Read Successful
		mov	word [es:di+1],ax	;Copy over Volume Size
		mov	ax,word [Buffer+82]
		mov	word [es:di+3],ax
		mov	ax,0			;Set Return Status = success
VolExit:
		TRACER 'v'
		retn


;=============================================================================

MedChng:			;Return Media Changed Status

		TRACER 'M'
		call	PriVolDesc		;Get and Check Primary Volume
						;  Descriptor
		mov	byte [es:di+1],-1	;Assume Media Changed
		mov	ax,800Fh		;  and Invalid Disk Change
		jc	MedExit			;If Media Changed or Bad

		mov	byte [es:di+1],1	;Media has not changed
		mov	ax,0			;Set Return Status = success
MedExit:
		TRACER 'm'
		retn


;=============================================================================

PriVolDesc:			;Get and Check Primary Volume
						;  Descriptor
		TRACER 'P'
		mov	ax,cs			;Set ds:si --> SpecPkt
		mov	ds,ax

		mov	cx, 5
PriVolAgain:
		mov	byte [SpecPkt],16	;SpecPkt Size
		mov	byte [SpecPkt+1],0	;Reserved
		mov	word [SpecPkt+2],1	;Transfer one 2048-byte sector
		push	cx
		mov	cl,byte [ReadBytes]	;Multiply by 4 if reading 512
		shl	word [SpecPkt+2],cl	;  bytes at a time
		pop	cx
		mov	word [SpecPkt+6],cs	;Into our Buffer
		mov	word [SpecPkt+4], Buffer
		mov	word [SpecPkt+8],16	;From CD Sector 16
		mov	word [SpecPkt+10],0
		mov	word [SpecPkt+12],0
		mov	word [SpecPkt+14],0

		mov	si, SpecPkt
		mov	dl, [DriveNumber]
		mov	ah, 42h			;Extended Read
		int	13h
		jnc	PriVolPass		;If success

;		TRACER '1'
		; read error
		loop	PriVolAgain

		TRACER '2'
		; read retries exhausted
		; flow into below
		jmp	PriReadErr

PriVolPass:
		mov	si,Buffer	;Point input to Buffer
		mov	ax,-1			;Init Checksum registers
		mov	bx,ax			;  bx,ax = 0FFFFFFFFh
		jc	PriNew			;If Read Failure

		push	di			;Read Successful,
						;  so Calculate Checksum
		mov	di,1024			;Init Word counter
PriWord:	mov	dx,[cs:si]		;Grab next word from buffer
		mov	cx,16			;Init bit counter
PriBit:		shr	dx,1			;Shift everything right 1 bit
		rcr	bx,1
		rcr	ax,1
		jnc	NoMult			;If a zero shifted out

		xor	bx,RPolyH		;A one shifted out, so XOR
		xor	ax,RPolyL		;  Checksum with RPoly
NoMult:
		loop	PriBit

		add	si,2			;Inc Word Pointer
		dec	di
		ja	PriWord
		TRACER '3'

		pop	di			;Checksum calculation complete
		cmp	bx,[Checksum+2]		;Has Checksum changed?
		jne	PriNew			;If Checksum Changed

		cmp	ax,[Checksum]
		jne	PriNew			;If Checksum Changed

		clc				;Checksum not changed, CF=0
		mov	ax,0			;Status = success
		jmp	PriOld

PriReadErr:
		mov	WORD [Checksum+2],bx		;Save New Checksum
		mov	[Checksum],ax		;  or 0FFFFFFFFh if bad read
		stc				;Checksum change, CF=1
		mov	ax, 800bh		;Status = read fault
		jmp	PriOld

PriNew:		mov	WORD [Checksum+2],bx		;Save New Checksum
		mov	[Checksum],ax		;  or 0FFFFFFFFh if bad read
		stc				;Checksum Changed, CF=1
		mov	ax,800Fh		;Status = Invalid Media Change
PriOld:
		TRACER 'p'
		retn


;=============================================================================

IoctlOutput:			;IOCTL Output Routine

		TRACER 'O'
		mov	di,[es:bx+14]		;es:bx --> Request Header
		mov	es,[es:bx+16]		;Get Xfer Address into es:di
		xor	ax,ax			;Get Control Block Code
		mov	al,[es:di]
		cmp	al,2
		jne	UnkIoctlO		;If not 2 (ResetDrv)
		call	DoNothing		;Reset Drive
		jmp	IoctlODone
UnkIoctlO:
		call	Unsupported		;Unsupported command
IoctlODone:
		TRACER 'o'
		retn


;=============================================================================

DoNothing:			;Do Nothing Command

		mov	ax,0			;Set Return Status = success
		retn


;=============================================================================

ReadL:			;Read Long Command

		TRACER 'R'
		mov	ax,cs			;Set ds=cs
		mov	ds,ax
						;es:bx --> Request Header
		cmp	byte [es:bx+24],0	;Check Data Read Mode
		jne	ReadLErr		;If Cooked Mode

		cmp	byte [es:bx+13],2	;Check Addressing Mode
		jb	ReadLOK			;If HSG or Redbook Mode

ReadLErr:
		TRACER '8'
		mov	ax,8003h		;Set Return Status = Unknown
		jmp	ReadLExit		;  Command Error and exit

ReadLOK:
		mov	ax,[es:bx+20]		;Get Starting Sector Number,
		mov	dx,[es:bx+22]		;  Assume HSG Addressing Mode
		cmp	byte [es:bx+13],0	;Check Addressing Mode again
		je	ReadLHSG		;If HSG Addressing Mode

		TRACER '7'
		;Using Redbook Addressing Mode.  Convert to HSG format
		mov	al,dl			;Get Minutes
		mov	dl,60
		mul	dl			;ax = Minutes * 60
		add	al,byte [es:bx+21]	;Add in Seconds
		adc	ah,0
		mov	dx,75			;dx:ax =
		mul	dx			;  ((Min * 60) + Sec) * 75
		add	al,byte [es:bx+20]	;Add in Frames
		adc	ah,0
		adc	dx,0
		sub	ax,150			;Subtract 2-Second offset
		sbb	dx,0			;dx:ax = HSG Starting Sector

ReadLHSG:
		mov	word [SpecPkt+8], ax	;Store Starting
		mov	word [SpecPkt+10], dx	;  Sector Number
		mov	word [SpecPkt+12], 0	;  (HSG Format)
		mov	word [SpecPkt+14], 0

		mov	ax,[es:bx+14]		;Get Transfer Address
		mov	word [SpecPkt+4],ax
		mov	ax,[es:bx+16]
		mov	word [SpecPkt+6],ax

		mov	byte [SpecPkt],16	;Size of Disk Address Packet
		mov	byte [SpecPkt+1],0	;Reserved

		mov	cx, 5
ReadLAgain:
		mov	ax,[es:bx+18]		;Get number of sectors to read
		mov	word [SpecPkt+2],ax
		cmp	ax, 3FFFh		;Too large?
		ja	ReadLBad		;If yes

		push	cx
		mov	cl,byte [ReadBytes]	;Multiply by 4 if reading 512
		shl	word [SpecPkt+2],cl	;  bytes at a time
		pop	cx

%ifdef DEBUG
		push	ax
		push	cx
		push	si
		mov	cx, 16
		mov	si,SpecPkt
ReadDump:	mov	al, ' '
		call	print_char
		mov	al, byte [si]	;Hexdump a SpecPkt byte
		call	print_hex8
		inc	si			;Point to next byte
		loop	ReadDump
		pop	si
		pop	cx
		pop	ax
%endif
		mov	si,SpecPkt
		mov	dl,[DriveNumber]
		mov	ah,42h			;Extended Read
		int	13h
		jnc	ReadLGd			;If success

;hang:
;		jmp	hang
;		TRACER '1'
		loop	ReadLAgain
		TRACER '2'
		jmp short ReadLBad
ReadLGd:
		TRACER '3'
		xor	ax, ax 			;Status 0 = success
		jmp short ReadLExit

ReadLBad:
		TRACER '9'
		mov	ax, 800Bh		;Set Read Fault Error
		; flow into ReadLExit
ReadLExit:
		TRACER 'r'
		retn



%ifdef DEBUG_TRACERS
debug_tracer:	pushad
		pushfd

		mov	al, '['
		mov	ah,0Eh			;BIOS video teletype output
		xor	bh, bh
		int	10h			;Print it

		mov	bp,sp
		mov	bx,[bp+9*4]		; Get return address
		mov	al,[cs:bx]		; Get data byte
		inc	word [bp+9*4]	; Return to after data byte

		mov	ah,0Eh			;BIOS video teletype output
		xor	bh, bh
		int	10h			;Print it

		mov	al, ']'
		mov	ah,0Eh			;BIOS video teletype output
		xor	bh, bh
		int	10h			;Print it

		popfd
		popad
		retn
%endif

;-----------------------------------------------------------------------------
; PRINT_HEX4
;-----------------------------------------------------------------------------
; print a 4 bits integer in hex
;
; Input:
;	AL - 4 bits integer to print (low)
;
; Output: None
;
; Registers destroyed: None
;
print_hex4:

	push	ax
	and	al, 0fh		; we only need the first nibble
	cmp	al, 10
	jae	hex_A_F
	add	al, '0'
	jmp	hex_0_9
hex_A_F:
	add	al, 'A'-10
hex_0_9:
	call	print_char
	pop	ax
	retn


;-----------------------------------------------------------------------------
; print_hex8
;-----------------------------------------------------------------------------
; print	a 8 bits integer in hex
;
; Input:
;	AL - 8 bits integer to print
;
; Output: None
;
; Registers destroyed: None
;
print_hex8:

	push	ax
	push	bx

	mov	ah, al
	shr	al, 4
	call	print_hex4

	mov	al, ah
	and	al, 0fh
	call	print_hex4

	pop	bx
	pop	ax
	retn


;=============================================================================
; print_hex16 - print a 16 bits integer in hex
;
; Input:
;	AX - 16 bits integer to print
;
; Output: None
;
; Registers destroyed: None
;=============================================================================
print_hex16:

	push	ax
	push	bx
	push	cx

	mov	cx, 4
print_hex16_loop:
	rol	ax, 4
	call	print_hex4
	loop	print_hex16_loop

	pop	cx
	pop	bx
	pop	ax
	retn

;=============================================================================
; print_hex32 - print a 32 bits integer in hex
;
; Input:
;	EAX - 32 bits integer to print
;
; Output: None
;
; Registers destroyed: None
;=============================================================================
print_hex32:

	push	eax
	push	bx
	push	cx

	mov	cx, 8
print_hex32_loop:
	rol	eax, 4
	call	print_hex4
	loop	print_hex32_loop

	pop	cx
	pop	bx
	pop	eax
	retn

;=============================================================================
; print_string - print string at current cursor location
;
; Input:
;	DS:SI - ASCIIZ string to print
;
; Output: None
;
; Registers destroyed: None
;=============================================================================
print_string:
		push	ax
		push	si

print_string_again:
		mov	al, [si]
		or	al, al
		jz	print_string_exit
		call	print_char
		inc	si
		jmp	print_string_again

print_string_exit:
		pop	si
		pop	ax
		retn

;-----------------------------------------------------------------------------
; PRINT_CHAR
;-----------------------------------------------------------------------------
; Print's a character at current cursor position
;
; Input:
;	AL - Character to print
;
; Output: None
;
; Registers destroyed: None
;
print_char:

		push	ax
		push	bx

		mov	ah,0Eh			;BIOS video teletype output
		xor	bh, bh
		int	10h			;Print it

print_char_exit:
		pop	bx
		pop	ax
		retn


;=============================================================================

;This space is used as a 2048-byte read buffer plus one test byte.
;The 96h data is used for testing the number of bytes returned by an Extended
;  CD-ROM sector read

		align	16, db 0
Buffer		times	2049	db	96h

;=============================================================================

Init:			;Initialization Routine

		TRACER 'I'
		mov	ax,cs			;ds=cs
		mov	ds,ax

%ifdef DEBUG
; print CS value (load segment)
		call	print_hex16
%endif

		mov	si, Greeting	;Display Greeting
		call	print_string

		mov	ax,Unsupported	;Init is executed only once
		mov	[Routines],ax

		mov	ax, 5400h
		int	13h			; Get diskemu status
		jc	FindBoot		; If CF=1 no diskemu loaded

		mov	[DriveNumber], cl		; Store drive number

		call	keyflag
		and	al, 8			; alt key ?
		jz	extread

		mov	si, DrvNumMsg	; Display "drive number="
		call	print_string
		mov	al, [DriveNumber]
		call	print_hex8
		mov	si, LineEnd	; CR/LF
		call	print_string
		jmp	extread

; Diskemu is not loaded
; so loop to find drive number
		; *** start of 1.4 changes ***
		; ??? mov dl, 0ffh		;Start at Drive 0xff
		; *** FindBoot at c47 in 1.4, at c0c in 1.3 ***
FindBoot:	call	ScanDrives		; call new helper in 1.4
		jnc	FoundBoot		; ded*df3
;		mov	si,offset SpecPkt	;Locate booted CD-ROM drive
;		mov	[SpecPkt],0		;Clear 1st byte of SpecPkt
;		mov	ax,4B01h		;Get Bootable CD-ROM Status
;		int	13h
;		jnc	FindPass		;If booted CD found
;
; Carry is not cleared in buggy Dell BIOSes,
; so I'm checking packet size byte
; some bogus bioses (Dell Inspiron 2500) returns packet size 0xff when failed
; Dell Dimension XPsT returns packet size 0x14 when OK

;		cmp	[SpecPkt], 0
;		jne	FoundBoot

;		cmp	[SpecPkt], 13h	; anything between 13h and 20h should be OK
;		jb	FindFail
;		cmp	[SpecPkt], 20h
;		ja	FindFail
;		jmp	short FoundBoot
;
; FindFail:
;		dec	dl			;Next drive
;		cmp	dl, 80h
;		jae	FindBoot		;Check from ffh..80h
		; *** end of 1.4 changes ***

		mov	si,NoBootCD	;No booted CD found,
		call	print_string
		jmp	NoEndAddr		;Do not install driver

FoundBoot:
;		mov	dl, [SpecPkt+2]		; 1.4 change
		; *** next line at c57 in 1.4, at c3d in 1.3 ***
		mov	[DriveNumber],dl		;Booted CD-ROM found,
						;  so save Drive #

		call	keyflag
		and	al, 8			; alt key ?
		jz	extread

		mov	si, CDStat
		call	print_string
		mov	si, SpecPkt	;Point to returned CD SpecPkt
		mov	cx, 19			;  containing 19 bytes
StatDump:	mov	al, ' '			;Print a space
		call	print_char
		mov	al, byte [si]	;Hexdump a SpecPkt byte
		call	print_hex8
		inc	si			;Point to next byte
		loop	StatDump

		mov	si, LineEnd	;Print a CR/LF
		call	print_string

extread:
;See how many CD Sector bytes are returned by an Extended Read
		mov	byte [SpecPkt],16	;SpecPkt Size
		mov	byte [SpecPkt+1],0	;Reserved
		mov	word [SpecPkt+2],1	;Transfer one sector
		mov	word [SpecPkt+6],cs	;Into our Buffer
		mov	word [SpecPkt+4],Buffer
		mov	word [SpecPkt+8],16	;From CD Sector 16
		mov	word [SpecPkt+10],0
		mov	word [SpecPkt+12],0
		mov	word [SpecPkt+14],0

		mov	si, SpecPkt	;Set ds:si --> SpecPkt
		mov	dl, [DriveNumber]
		mov	ah, 42h			;Extended Read
		int	13h
		jnc	SecSize			;If success

		mov	ah, 42h			;Always make 2 read attempts
		int	13h
						;How many bytes did we get?
SecSize:	std				;Count down
		mov	ax,cs			;Point to end of Buffer
		mov	es,ax
		mov	di,Buffer+2047	;Find end of read data
		mov	si,Buffer+2048
		mov	cx,2049
		repe	cmpsb			;cx = number of bytes read

		cld				;Restore count direction to up
		mov	si,CDBytes	;Display number of bytes read
		call	print_string

		mov	al, [DriveNumber]
		call	print_hex8

		mov	si,CDBytesA	;Remainder A of message
		call	print_string

		mov	al,ch			;Hex-dump cx
		and	al,0Fh			;Second nibble
		call	print_hex8		;  (don't need the First)
		mov	al,cl
		call	print_hex8		;  (don't need the First)

		mov	si,CDBytesB	;Remainder B of message
		call	print_string

		cmp	cx,2048			;Did we read 2048 bytes?
		je	ParseParm		;If yes <-- O.K.

		mov	byte [ReadBytes],1
		cmp	cx,1024			;Did we read 1024 bytes?
		je	ParseParm		;If yes <-- O.K.

		mov	byte [ReadBytes],2
		cmp	cx,512			;Did we read 512 bytes?
		jne	NoEndAddr		;If not, do not load driver

ParseParm:	mov	bx,word [cs:ReqHdrLoc]	;Parse command line
		mov	es,word [cs:ReqHdrLoc+2]	;  parameters
		mov	si,[es:bx+18]		;Get BPB array ptr into DS:SI
		mov	ds,[es:bx+20]
FindParm:	inc	si
FindParm1:	cmp	byte [si],0Dh	;CR? (End of parameters)
		je	EndOfParms

		cmp	byte [si],0Ah	;LF?
		je	EndOfParms

		cmp	byte [si],'/'	;A parameter?
		jne	FindParm

		inc	si
		cmp	byte [si],'D'	;Device Name parameter?
		jne	FindParm1

		inc	si
		cmp	byte [si],':'
		jne	FindParm1

;bbb
		push	si
		mov	si, DevName	;Device Name is at ds:si
		push	ds			;Keep ptr to Device Name
		mov	ax, cs
		mov	ds, ax
		call	print_string
		pop	ds			;Retrieve Device Name ptr
		pop	si
		mov	cx, 8			;Get next 8 chars
		inc	si			;  = Device Name
		mov	ax, cs
		mov	es, ax
		mov	di, DeviceName
NextChar:	cmp	byte [si],' '
		ja	AboveSpace

		mov	ax,cs			;Pad end of Device Name with
		mov	ds,ax			;  spaces if necessary
		mov	si,DblSpace	;A space
AboveSpace:	mov	al, [si]
		call	print_char
		movsb				;ds:[si] --> es:[di]
		loop 	NextChar

		mov	si,LineEnd
		mov	ax,cs
		mov	ds,ax
		call	print_string

		mov	ax,Init-2	;Last byte of driver to keep
		jmp	EndAddr			;Install driver

EndOfParms:
		mov	ax, cs			; Restore segment registers (fix)
		mov	ds, ax
		mov	es, ax

		mov	si,NoDevName	;No Device Name Found
		call	print_string

NoEndAddr:	mov	ax,0			;Do not install driver

EndAddr:	mov	es,[ReqHdrLoc+2]		;Write End Address
		mov	bx,[ReqHdrLoc]
		mov	[es:bx+14],ax
		mov	[es:bx+16],cs
		mov	bx,ax			;Hold onto install status

		mov	si, DrvInst	;Display driver install status
		call	print_string
		mov	si, DrvInst1	;Assume driver installed
		cmp	bx,0			;Was driver installed?
		jne	DrvStatus		;If yes
		mov	si, NoDrvInst	;Driver not installed
DrvStatus:	call	print_string

		mov	ax,0			;Set Return Status = success
		cmp	bx,0			;Was INIT successful?
		jne	InitStat		;If yes
		mov	ax,800Ch		;Status = General Failure
InitStat:
		push	ax			;Save Return Status

		call	keyflag
		and	al, 8			; alt key ?
		jz	InitExit

WaitHere:
		mov	si, WaitMsg	;Display Halted message
		call	print_string

AltWait:
		call	keyflag
		and	al, 8			; Alt key?
		jnz	AltWait			; Pressed? yes -> wait

InitExit:
		pop	ax			;Retrieve Return Status
		TRACER 'i'
		retn				;That's it for Init!

		; *** start 1.4 changes at ded ***
SpecGo:		mov	si,SpecPkt
		int	13h
		retn

ScanDrives:	push	ax		; at df3 in 1.4
		push	si
		mov dl, 7fh		;Start at Drive 0x80
NextDrv:	inc	dl
		clc
		mov	ax,4B01h	;Get Bootable CD-ROM Status
		mov	BYTE [SpecPkt],0	;Clear 1st byte of SpecPkt
		call	SpecGo
; Carry is not cleared in buggy Dell BIOSes,
; so I'm checking packet size byte
; some bogus bioses (Dell Inspiron 2500) returns packet size 0xff when failed
; Dell Dimension XPsT returns packet size 0x14 when OK

		cmp	BYTE [SpecPkt], 13h	; anything between 13h and 20h should be OK
		jb	FindFail
		cmp	BYTE [SpecPkt], 20h
		ja	FindFail	; in 1.4 at e16
		jmp	short SendFound	; in 1.4 at e26

FindFail:	cmp	dl, 0ffh
		je	SendFail		; Check from 80h..ffh
		jmp	short NextDrv		;Next drive
SendFail:	xor	dl,dl
		stc
		jmp	short ThingDone
SendFound:	mov	dl, [SpecPkt+2]
		clc
ThingDone:	pop	si
		pop	ax
		retn
		; *** end 1.4 changes ***

;=============================================================================

;------------------------------------------------------------
; keyboard flags - return keyboard flags in AL
; bit 3 = ALT key
keyflag:	; at dbc in 1.3, at e2e in 1.4
	push	bx
	mov	ah, 2
	int	16h
	pop	bx
	retn

;=============================================================================

DrvNumMsg	db	'  Diskemxx.bin returned drive number=', 0
NoBootCD	db	'  No booted CD-ROM found.',CR,0

CDStat		db	'  INT 13h / AX=4B01h Specification Packet for '
		db	'Booted CD-ROM:',CR,'     ', 0

CDBytes		db	'  Drive ', 0
CDBytesA	db	' returns ', 0
CDBytesB	db	'h bytes per Sector.',CR,0

DevName		db	'  Device Name: ', 0
NoDevName	db	'  No Device Name found. '
		db	'Usage: device=eltorito.sys /D:<DevName>',CR,0

DrvInst		db	'  Driver ', 0
NoDrvInst	db	7,'not '		;7 = Ctrl-G = Beep
DrvInst1	db	'installed',CR,0

WaitMsg		db	'  Alt pressed, waiting...', CR, 0
;ContMsg		db	'  Continuing...'
LineEnd		db	CR,0


;=============================================================================
