%include "util.mac"
%define WIN403SERVICES
%include "vxdn.inc"
%include "icedump.inc"
%include "vmp3d.inc"
%include "wiat.inc"


global Parse_MP3Player
global MP3MainHook
global Service_MP3Player


extern sdata
extern Parser.error
extern Parser.errorMsg
extern SetCB
extern ParseExpression


bits 32


;-------------------------------------------------------------------------------
; MP3 <0,n,+,->
; stop,play,next,prev
;-------------------------------------------------------------------------------
segment _LTEXT
Parse_MP3Player:
	mov	edi,.Error_NoPlayer
	cmp	byte [sdata+MP3.PlayerPresent],1
	jne	near Parser.errorMsg

; parse params
	call	[pSkipWhiteSpace]	; skip to command/track#
	mov	edi,.Error_NoParam
	jz	near Parser.errorMsg

	cmp	byte [esi],'+'
	jz	@F

	cmp	byte [esi],'-'
	jnz	.parseTrack

@@
	movzx	eax,byte [esi]
	mov	ebp,[dClient_EBX]
	mov	[ebp],eax
	jmp	short .setCB

.parseTrack:
	mov	edi,.Error_Track
	call	ParseExpression		; parse 0,n
	jc	near Parser.errorMsg

	mov	ebp,[dClient_ECX]
	mov	[ebp],eax

	mov	ebp,[dClient_EBX]
	mov	dword [ebp],0

.setCB:
	push	byte SERVICE_MP3PLAYER
	mov	ebp,[dClient_EAX]
	pop	dword [ebp]

	call	SetCB

	mov	ebp,[fExecuteMoreCommands]	; set internal Winice flag to 0
	mov	byte [ebp],0

	popad
	retn


segment _LDATA
.Error_NoPlayer: db 'MP3 player not loaded.',0
.Error_NoParam: db 'no params specified',0
.Error_Track: db 'could not parse track',0


;-------------------------------------------------------------------------------
; inside-winice IRQ handler
;-------------------------------------------------------------------------------
segment _STEXT
StaticSoundCardIntProc:
	cld
	push	edi
	mov	edi,[cs:sdata+s_fWiniceIsActive]
	cmp	byte [cs:edi],0
	pop	edi
	jnz	@F			; yes: delegate to winice

	push	dword [cs:sdata+OldSoundCardIntProc.sel]
	push	dword [cs:sdata+OldSoundCardIntProc.off]

	retf

@@
	pushad
	push	es
	push	ds

	mov	eax,0x30		; ring-0 data selector
	mov	ds,eax
	mov	es,eax

	VxDCall	VMP3D_On_SoftIce_IRQ

; send EOI
	movzx	eax,byte [sdata+MP3.nIRQ]
	call	[sdata+s_pSendSpecificEOI]

	pop	ds
	pop	es
	popad
	iretd


;-------------------------------------------------------------------------------
; WiniceMain hook
;-------------------------------------------------------------------------------
segment _LTEXT
MP3MainHook:
	cmp	byte [sdata+MP3.PlayerPresent],1
	je	.present

; reinit VxDCall, since VMM will replace our code
	mov	word [.checkmp3],0x20CD			; VxDCall
	GetDeviceServiceOrdinal dword [.checkmp3+2],VMP3D_Get_Version

.checkmp3:
	VxDCall	VMP3D_Get_Version
	jnc	@F

	debug_start debugflags, ICEDUMP_DEBUG_MP3
	mov	esi,.Msg_VMP3DNotFound
	call	[pPrintToCommandWindow]
	debug_end

	retn

@@
	debug_start debugflags, ICEDUMP_DEBUG_MP3
	mov	esi,.Msg_VMP3DFound
	call	[pPrintToCommandWindow]
	debug_end

	mov	byte [sdata+MP3.PlayerPresent],1

.present:
	debug_start debugflags, ICEDUMP_DEBUG_MP3
	mov	esi,.Msg_VMP3DPresent
	call	[pPrintToCommandWindow]
	debug_end

; Hook IRQx, our strategy is to install first level handlers (that is, directly
; into the IDT) into whatever IDT happens to be active at the time winice came
; up, this will eventually install it everywhere

	debug_start debugflags, ICEDUMP_DEBUG_MP3
	mov	esi,.Msg_IRQHooking
	call	[pPrintToCommandWindow]
	debug_end

	VxDCall	VMP3D_Get_IRQ
	mov	[sdata+MP3.nIRQ],al

	call	[pIRQ2INT]

	push	eax
	sidt	[esp-2]
	pop	edi
	lea	edi,[edi+8*eax]			; edi: IDT descriptor offset

	mov	bx,[edi+6]			; get old gate offset
	shl	ebx,16
	mov	bx,[edi]
	cmp	ebx,StaticSoundCardIntProc	; already hooked?
	jnz	@F

	cmp	word [edi+2],0x28
	jz	.hooked

@@
	cmp	[sdata+OldSoundCardIntProc.off],byte 0
	jz	@F

	cmp	[sdata+OldSoundCardIntProc.off],ebx
	jz	@F

	mov	esi,.Msg_DifferentHandler
	call	[pPrintToCommandWindow]

@@
	mov	[sdata+OldSoundCardIntProc.off],ebx

	movzx	ebx,word [edi+2]		; get old gate selector
	mov	[sdata+OldSoundCardIntProc.sel],ebx

	mov	ebx,StaticSoundCardIntProc	; write new gate offset
	mov	[edi],bx
	shr	ebx,16
	mov	[edi+6],bx

	mov	word [edi+2],0x28		; write new gate selector

	debug_start debugflags, ICEDUMP_DEBUG_MP3
	mov	esi,.Msg_IRQHooked
	call	[pPrintToCommandWindow]
	debug_end

.hooked:

; test if VMP3D is playing a song already, and enable IRQ handling
; otherwise silence... until user exits winice

	VxDCall	VMP3D_Need_SoftIce_IRQ
	jnz	@F

	debug_start debugflags, ICEDUMP_DEBUG_MP3
	mov	esi,.Msg_IRQNotEnabled
	call	[pPrintToCommandWindow]
	debug_end

	retn

@@

; ok, let's rock ;-)
	movzx	eax,byte [sdata+MP3.nIRQ]
	call	[pEnableIRQ]

	debug_start debugflags, ICEDUMP_DEBUG_MP3
	mov	esi,.Msg_IRQEnabled
	call	[pPrintToCommandWindow]
	debug_end

	retn


segment _LDATA
.Msg_VMP3DNotFound:	db 'VMP3D not found',0
.Msg_VMP3DFound:	db 'VMP3D found',0
.Msg_VMP3DPresent:	db 'VMP3D present',0
.Msg_IRQHooking:	db 'hooking IRQ',0
.Msg_IRQHooked:		db 'hooked IRQ',0
.Msg_IRQNotEnabled:	db 'did not enable IRQ',0
.Msg_IRQEnabled:	db 'enabled IRQ',0

.Msg_DifferentHandler:	db 'ERROR: old first level handler is different',0


;-------------------------------------------------------------------------------
; this is the actual MP3 Player code that's executed in ring-0.
;	EBP = CRS
;-------------------------------------------------------------------------------
segment _LTEXT
Service_MP3Player:
	cmp	dword [ebp+CRS.EBX],byte '+'
	jnz	@F

	VxDCall	VMP3D_Play_Next
	jmp	short .ret

@@
	cmp	dword [ebp+CRS.EBX],byte '-'
	jnz	@F

	VxDCall	VMP3D_Play_Prev
	jmp	short .ret

@@
	cmp	dword [ebp+CRS.EBX],byte 0
	jnz	.ret

	cmp	dword [ebp+CRS.ECX],byte 0
	jnz	@F

	VxDCall	VMP3D_Stop
	jmp	short .ret

@@
	mov	eax,[ebp+CRS.ECX]
	dec	eax
	VxDCall	VMP3D_Play

.ret:
	popfd
	popad
	retn
