#include "doshelpers.h"

.code16
.text
	.globl	_start16
_start16:

LinuxCD	=	DOS_HELPER_INT	# int 0xe6 for cdrom server
Helper  =       0x40		# code to reach cdrom server
cr	=	0x0d
lf	=	0x0a
eom	=	'$'


# header of a console device
ConDev:
	.long	-1		# pointer to next driver
	.word	0xc800		# driver attributes
        .word	Strategy        # pointer to strategy routine
        .word   Entry		# pointer to service routine
        .ascii  "MSCD000"	# logical device name
Index:	.ascii	"1"
	.word	0		# reserved
        .byte   0		# drive letter
        .byte   1		# number of units supported by driver
        .ascii  "MSCD00"	# signature

# command jump table
ConTbl:
	.word	coninit		# initialize driver
	.word	cmderr		# media check
	.word	cmderr		# build BPB
	.word	ioctlin		# input of control strings
	.word	cmderr		# input
	.word	cmderr		# input no wait
	.word 	cmderr		# input status
	.word	input_flush	# input flush
	.word 	cmderr		# output
	.word	cmderr		# output verify
	.word	cmderr		# output status
	.word	cmderr		# output flush
	.word	ioctlout	# output of control strings
	.word	dev_open	# device open
	.word 	dev_close	# device close
	.word 	cmderr		# check if media is removed
	.word  	cmderr		# out until busy
# now the commands defined by the cdrom extensions
MscdTbl:
	.word	read_long	# read data from cdrom drive
	.word	cmderr		# reserved
	.word	cmderr          # read long prefetch
        .word   seek            # position drives head
        .word   play            # play audio
        .word   stop            # stop playing audio
        .word   cmderr          # write long
        .word 	cmderr          # write verify
        .word   resume          # resume playing audio

ptrsav:	.long	0		# saved pointer to request header
arg:	.byte	0		# Number of CDROM drive 0 ... 3


# structure of command block header (13 bytes)
CmdLen	= 	0		# length of header
Unit   	=	1		# sub unit id
CmdCode	=	2		# describes command to be executed
Status	=	3		# status code
Reserved=	5		# bytes 5 to 12 are reserved

Strategy:
				# save adress of command block
	mov	%bx, %cs:ptrsav	# in ptrsav
	mov 	%es, %cs:ptrsav+2
	lret

Entry:
	pushw	%si
	pushw	%ax
	pushw	%cx
	pushw	%dx
	pushw	%di
	pushw	%bp
	pushw	%ds
	pushw	%es
	pushw	%bx

	pushw	%cs
	popw	%ds
	les	ptrsav,%di	# es:di now points to the command block

	movb	%es:CmdCode(%di), %al # get command code
	movb	$0,%ah
	test	$0x80,%al	# test if highes bit is set
	jz	StdCommand
	andb	$0x7F,%al	# AL = AL-128
	cmpb	$8,%al
	ja	cmderr
	movw	$MscdTbl,%si
	jmp	EntryCon

StdCommand:
	cmpb	$16,%al
	ja 	cmderr
	movw	$ConTbl,%si
EntryCon:
	addw	%ax,%si
	addw	%ax,%si
# now we have in es:di the adress of the command block and cs = ds
# now call command handler
	jmpw    *(%si)


cmderr:
	movw	$3,%ax		# unknown command
	movb    $0x80,%ah	# set error bit
exit:
# here we expect that %ax contains a valid status code
	orb	$0x01,%ah	# set done flag
	pushw	%cs
	popw	%ds
	les	ptrsav,%di      # pointer to command block to es:di

	mov 	%ax,%es:Status(%di)

	popw	%bx
	popw	%es
	popw	%ds
	popw	%bp
	popw	%di
	popw	%dx
	popw	%cx
	popw	%ax
	popw	%si
	lret


#-----------------------------------------------------------------------
# the device functions
#-----------------------------------------------------------------------


#-----------------------------------------
# device open / device close / input flush

dev_open:
	movw	$0,%ax
	jmp	exit
dev_close:
	movw	$0,%ax
	jmp	exit
input_flush:
	movw	$0,%ax
	jmp	exit


#----------------------------------------
# play / stop / resume

play:
	movb	$Helper, %al
	movb	$0x04, %ah
        orb	%cs:arg, %ah
	int	$LinuxCD
	cmp	$0, %al
	jne	play_fail

	movb	$0, %al
	movb	$0x02, %ah 	# set busy bit
	jmp	exit
play_fail:
	movb	$0x0B, %al	# set error code
	movb	$0x80, %ah 	# set error bit
	jmp	exit

stop:
	movb	$Helper, %al
	movb	$0x05, %ah
        orb	%cs:arg, %ah
	int	$LinuxCD
 	movw	$0,%ax
	jmp	exit
	
resume:
	movb	$Helper, %al
	movb	$0x06, %ah
        orb	%cs:arg, %ah
	int	$LinuxCD
	cmpb	$0, %al
	jne	resume_fail

	movb	$0, %al
	movb	$0x02, %ah
	jmp	exit
resume_fail:
	movb	$0x0C, %al	# set error code
	movb	$0x80, %ah	# set error bit
	jmp	exit

	
#----------------------------------------
# read long / seek

seek:
	movb	$Helper, %al
	movb	$0x03, %ah
        orb	%cs:arg, %ah
	int	$LinuxCD
	movw	$0,%ax
	jmp	exit

READLONG_TRANSFER = 14

read_long:
	movb	$Helper, %al
	movb	$0x02, %ah
        orb	%cs:arg, %ah
	lds	%es:READLONG_TRANSFER(%di), %si
	int	$LinuxCD
	cmpb	$0, %al
	jne	read_long_fail

	movw	$0,%ax
	jmp	exit

read_long_fail:
        movb	$0x0F, %al	# set error code
	movb	$0x80, %ah	# set error bit
	jmp	exit


#----------------------------------------
# ioctlout

# structure of an ioctlout request
IOCTLOUT_MDB	= 13		# media descriptor byte (not used)
IOCTLOUT_TRANS	= 14		# transfer adress
IOCTLOUT_TRANSLEN= 18		# number of bytes to be transfered
IOCTLOUT_SEKTOR  = 20		# not used
IOCTLOUT_VOL     = 22		# not used (4 bytes)	

# ioctlout jump table
IoctloutTbl:	.word	eject
		.word	lockunlock
		.word	resetdrive
		.word	audio_ctrl
		.word	cmderr
		.word	closetray

ioctlout:
	lds	%es:IOCTLOUT_TRANS(%di),%si

# now we have:
#   es:di points to command control block
#   ds:si points to request header
#   cs!=ds

	movb	(%si),%al	# get request code
	movb	$0,%ah
	cmpw	$5,%ax
	ja	ioctlout_err

	pushw	%cs
	popw	%es
	movw	$IoctloutTbl,%di
	add	%ax,%di
	add	%ax,%di

	jmpw	*%es:(%di)

ioctlout_err:
	jmp	cmderr

# eject disk
eject:
	les	%cs:ptrsav,%di
	movb	$Helper,%al
	movb	$0x0D,%ah
	orb	%cs:arg,%ah
	int	$LinuxCD

	mov	$0,%ax
	jmp	exit

# function lock - unlock drive
lockunlock:
	les	%cs:ptrsav,%di
	movb	$Helper,%al
	movb	$0x0C,%ah
	orb	%cs:arg,%ah
        movb	1(%si),%bl
	int	$LinuxCD

	mov	$0,%ax
	jmp	exit

# resetdrive
resetdrive:
	les	%cs:ptrsav,%di
	movb	$Helper,%al
	movb	$0x0B,%ah
	orb	%cs:arg,%ah
	int	$LinuxCD

	mov	$0,%ax
	jmp	exit

# function closetray
closetray:
	les	%cs:ptrsav,%di
	movb	$Helper,%al
	movb	$0x0E,%ah
	orb	%cs:arg,%ah
	int	$LinuxCD

	mov	$0,%ax
	jmp	exit

# alter audio control
audio_ctrl:
	les	%cs:ptrsav,%di
	movb	$Helper,%al
	movb	$0x0F,%ah
	orb	%cs:arg,%ah
	int	$LinuxCD

	jmp 	check_busy


#----------------------------------------
# ioctlin

# structure of an ioctlin request
IOCTLIN_MDB	= 13		# media descriptor byte (not used)
IOCTLIN_TRANS	= 14		# transfer adress
IOCTLIN_TRANSLEN= 18		# number of bytes to be transfered
IOCTLIN_SEKTOR  = 20		# not used
IOCTLIN_VOL     = 22		# not used (4 bytes)	

# ioctlin jump table
IoctlinTbl:	.word	devheader_adress
		.word	lochead
		.word	cmderr
		.word	cmderr
		.word	getchaninfo
		.word	cmderr
		.word	devstatus
		.word	ret_sectorsize
		.word	ret_volumesize
		.word	media_changed
		.word	diskinfo
		.word	trackinfo
		.word	qchannel
		.word	cmderr
		.word 	cmderr
		.word	audiostatus

ioctlin:
	lds	%es:IOCTLIN_TRANS(%di),%si

# now we have:
#   es:di points to command control block
#   ds:si points to request header
#   cs!=ds

	movb	(%si),%al	# get request code
	movb	$0,%ah
	cmp	$15,%ax
	ja	ioctlin_err

	pushw	%cs
	popw	%es
	mov	$IoctlinTbl,%di
	add	%ax,%di
	add	%ax,%di

	jmp	*%es:(%di)

ioctlin_err:
	jmp	cmderr

# function get device header adress
HEADER_ADRESS	= 1

devheader_adress:
	movw	$ConDev,%ax
	pushw	%cs
	popw	%bx
	movw	%ax, HEADER_ADRESS(%si)
	movw	%bx, HEADER_ADRESS+2(%si)
	movw	$0, %ax
	jmp	exit

# get device status
DEVICE_PARMS = 1

devstatus:
	les	%cs:ptrsav,%di
	movb	$Helper,%al
	movb	$0x0A,%ah
	orb	%cs:arg, %ah
	int	$LinuxCD

	movw	%bx, DEVICE_PARMS(%si)
	movw	$0, DEVICE_PARMS+2(%si)
	jmp	check_busy

# return sectorsize
RET_SECTORSIZE_READMODE	= 1
RET_SECTORSIZE_SIZE	= 2

ret_sectorsize:
	les	%cs:ptrsav,%di
	movb	$Helper,%al
	movb	$0x08,%ah
	orb	%cs:arg, %ah
	int	$LinuxCD

	movw	%bx,RET_SECTORSIZE_SIZE(%si)
	movw	$0,%ax
	jmp	exit

# return volumesize
ret_volumesize:
	les	%cs:ptrsav,%di
	movb	$Helper,%al
	movb	$0x12,%ah
	orb	%cs:arg, %ah
	int	$LinuxCD

	movw	$0,%ax
	jmp	exit

# media changed ?
MEDIA_BYTE = 1

media_changed:
	les	%cs:ptrsav,%di
	movb	$Helper,%al
	movb	$0x9,%ah
	orb	%cs:arg, %ah
	int	$LinuxCD

	cmp	$0,%bl
	jne	media_changed_dnr
	movb	$1,%bh			# media not changed/disk ready
	movb	%bh,MEDIA_BYTE(%si)
	jmp	check_busy
media_changed_dnr:
	movb	$0xff,%bh                # media changed
	movb	%bh,MEDIA_BYTE(%si)
	jmp	check_busy

# location of head
lochead:
	les	%cs:ptrsav,%di
	movb	$Helper,%al
	movb	$0x7,%ah
	orb	%cs:arg, %ah
	int	$LinuxCD

	jmp	check_busy

# get status of audio
audiostatus:
	les	%cs:ptrsav,%di
	movb	$Helper,%al
	movb	$0x14,%ah
	orb	%cs:arg, %ah
	int	$LinuxCD

	jmp	check_busy

# get q-channel information
qchannel:
	les	%cs:ptrsav,%di
	movb	$Helper,%al
	movb	$0x13,%ah
	orb	%cs:arg, %ah
	int	$LinuxCD

	jmp	check_busy

# get information about tracks
trackinfo:
	les	%cs:ptrsav,%di
	movb	$Helper,%al
	movb	$0x11,%ah
	orb	%cs:arg, %ah
	int	$LinuxCD

	jmp	check_busy

# get information about tracks
diskinfo:
	les	%cs:ptrsav,%di
	movb	$Helper,%al
	movb	$0x10,%ah
	orb	%cs:arg,%ah
	int	$LinuxCD

	jmp	check_busy

# get information about channels
getchaninfo:
	les	%cs:ptrsav,%di
	movb	$Helper,%al
	movb	$0x15,%ah
	orb	%cs:arg, %ah
	int	$LinuxCD

	jmp	check_busy


# audio playing in progress ?
# hier noch irgendwie eine Fehlerbehandlung einbauen....


check_busy:
        cmp     $1,%al
        je      error
	cmp	$1,%ah
	je	busy
	movw	$0,%ax
	jmp	exit
busy:
	movb	$0,%al
	movb	$0x02, %ah
	jmp	exit
error:
	movb	$0x0C, %al
	movb	$0x80, %ah	# set error bit
	jmp	exit


#----------------------------------------
# init (must be at the end!)

# structure of an init request:
INIT_NumOfUnits	= 13
INIT_EndAdress	= 14
INIT_BPB	= 18
INIT_BDN	= 22


Message:
	.ascii	"dosemu CDROM driver installed (V0.2)"
	.byte	cr,lf,eom
FailMsg:
	.byte	cr,lf,lf
	.ascii  "Access to cdrom denied."
	.byte	cr,lf
	.ascii	"Installation aborted."
	.byte	cr,lf,lf,eom

coninit:
	jmp 	ParseArgs
cont:	mov	$Message, %dx
	movb	$0x09, %ah
	int	$0x21

 	movb	$0x01, %ah
	orb	%cs:arg, %ah
	movb	$Helper, %al
	int	$LinuxCD
	cmp	$0, %al
	je	InitEnde
	movw	$FailMsg, %dx
	movb	$0x09, %ah
	int     $0x21
InitEnde:
	movw	$Message, %es:INIT_EndAdress(%di)
        movw	%cs, %ax
	movw	%ax, %es:INIT_EndAdress+2(%di)
# future calls to init must return in error
	movw	$cmderr, ConTbl

	movw	$0, %ax
	jmp     exit

ParseArgs:
	pushw 	%ds 		#On entry es:di points to the command line
	pushw 	%si		#the relevant data start at offset 0x12
	pushw	%ax		#If we have _no_ parameter, the default
				#will be drive 0 and name MSCD0001
	pushw 	%es
	popw	%ds
	lds 	0x012(%di),%si	#ds:si now pointing to our paramter
	
	jmp IniParse

EndParseArgs:
	popw	%ax
	popw	%si
	popw	%ds
	jmp 	cont

IniParse:
	cmpb	$0x20,(%si)
	jb	EndParseArgs
	jz	Label
	incw	%si
	jmp	IniParse


Label:	incw 	%si

StartParse:
	cmpb 	$' ',(%si)
	jb 	EndParseArgs
	jz 	NextChar

	cmpb	$'1',(%si)		#lowest allowed value is '1';
	jb	NextChar		#if not get next character
	cmpb	$'4',(%si)		#highest allowed value is '4'
	ja	NextChar		#if higher get next character
#					#if lower all OK
	movb	(%si),%al
	movb	%al,%cs:Index		#modify the device name ('1'...'4')
	subb 	$0x31,%al		#calculate the index 0..3
	shlb	$6,%al
	movb	%al,%cs:arg

	jmp	EndParseArgs

NextChar:
	incw 	%si
	jmp	StartParse
