*:ts=8
*:fo=excel,11


* :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
* ::::::::::::::::###::::######::::::::::#:::::::::##::::::::
* :####::::::::####:::::########:::::::::##::::::::##::::::::
* :###############:::::##::::::::::::::::###::::::###::::::::
* :::::#######:::::::::##:::::::::::::::::##::::::###::::::::
* ::::::::##:::::::::::#::::::::::::::::::###:::::#:#::::::::
* ::::::::#::::::::::::##:::::::::::::::::####:::##:#::::::::
* :::::::##::::::::::::##::::::::::::::::::###:::#::#::::::::
* :::::::##:::::::::::::##:::::::::::::::::#:##::#::#::::::::
* :::::::#:::::::::::::::##::::::::::::::::#::####::##:::::::
* ::::::##::::::::::::::::###::::::::::::::#:::##:::##:::::::
* ::::::##:::::::::::::::::###:::::::::::::#:::::::::#:::::::
* ::::::##::::::::::::::::::###::::::::::::#:::::::::##::::::
* ::::::##:::::::::::::::::::##:::::::::::##:::::::::##::::::
* ::::::##::::::::::::::::::::##::::::::::##::::::::::#::::::
* ::::::##:::::::::::::::::::::#::::::::::##::::::::::##:::::
* ::::::##:::::::::::::::::::::##:::::::::#::::::::::::#:::::
* ::::::##:::::::::::::::::::::##::::::::##::::::::::::##::::
* ::::::##:::::::::::::::::::::##::::::::##:::::::::::::##:::
* ::::::##::::::::::::::::::::##::::::::##:::::::::::::::##::
* :::::::#::::::::::::#########::::::::##::::::::::::::::###:
* :::::::#:::::::::::::#######::::::::###::::::::::::::::::#:
* :::::::#:::::::::::::::::::::::::::::#:::::::::::::::::::::
* ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: version 1.2


*
** This source-code for MCC and compatible assemblers.
** This stuff is Amiga-ROM® specific and re-entrant.
** It was written through the use of HiSoft's Devpac Amiga 3 assembler.
*
** One of the few programs ever written for the Amiga that when resident
** will detach from CLI/Shell.
*
** Please note: Cannot run multiple times on the same console-task
** otherwise strange things happen - because of an installed hook
** for process entry »PktWait« !
*

*
** © Copyright 10 & 12/1998, 11/1999	Joerg van de Loo
**					Hoevel 15
**					47559 Kranenburg
**					Germany
*

*
** This is   N O T   P U B L I C   D O M A I N   S O F T W A R E  !
*

*
** The problem: How to get informed when the console window is closed by user.
** For what:    To remove a console window from list of Workbench AppWindows.
** Remedy:	This file ... (TSM)
*

	IFD	__G2	; Devpac 2 (__G2 (BYTE) >= 43 == Devpac 3)
		MACHINE	MC68000
		OUTPUT		RAM:TSM
		IDNT 'Tame Shell Master'
		OPT	OW-
	ENDC

	include	exec/exec_lib.i
	include	exec/memory.i

	include	dos/dos_lib.i
	include	dos/dos.i
	include	dos/dosextens.i

	include	intuition/intuition_lib.i

	include	workbench/wb_lib.i
	include	workbench/startup.i
	include	workbench/workbench.i

	include	libraries/commodities_lib.i
	include	libraries/commodities.i

	include	devices/input.i
	include	devices/inputevent.i


	STRUCTURE	BaseTable,0
	APTR		nada			; In german (nix da bzw. nichts)
	APTR		_SysBase
	APTR		_ThisTask
	ULONG		_errno
	APTR		_DOSBase
	APTR		__ResSegment
	APTR		__PgmName
	ULONG		__PgmNameSize
* ------------------------------------
	APTR		_IntuitionBase
	APTR		_WBenchBase
	APTR		_CxBase
	BPTR		_ConHandle
	APTR		_WinPtr
	APTR		_ConTask
	APTR		_ConPort
	APTR		_MsgPort
	APTR		_OldPort
	STRUCT		_DummyMessage,16	; 14 required
	APTR		_AppPort
	APTR		_AppWin
	APTR		_ReplyPort
	STRUCT		_InputIOReq,IOSTD_SIZE
	STRUCT		_InputEvent,ie_SIZEOF
	STRUCT		_StrStorage,254
	LABEL		BT_SIZEOF


BREL	EQU	$8000

get	MACRO
	move.\0	\1-BREL(A4),\2
	ENDM

geta	MACRO
	movea.\0 \1-BREL(A4),\2
	ENDM

put	MACRO
	move.\0	\1,\2-BREL(A4)
	ENDM

adr	MACRO
	lea	\1-BREL(A4),\2
	ENDM

taz	MACRO			Test against zero
	tst.\0	\1-BREL(A4)
	ENDM

qmove	MACRO
	ifgt	\1-255
	move.\0	#\1,\2
	mexit
	endc

	ifle	\1-127
	moveq	#\1,\2
	mexit
	endc

	moveq	#255-\1,\2
	not.b	\2
	ENDM



;DEBUG	SET	1

	IFD	DEBUG
		IFD	__G2
		OPT	D+
		ENDC

	XDEF	__start_me_up
	XDEF	_FindSegment
	XDEF	__exitNoProc
	XDEF	__exitNoPgmNameMem
	XDEF	__exitNoOutput
	XDEF	__exitDOSOpen
	XDEF	__exitNoDOS
	XDEF	__exitReal
	XDEF	_DOSName
	XDEF	_IntName
	XDEF	_WBenchName
	XDEF	_CxName
	XDEF	_Con

	XDEF	__createStartup
	XDEF	_AwaitCloseDown
	XDEF	_ReactToMsg
	XDEF	_WriteIE
	XDEF	_NewPktWait
	XDEF	_RemPort
	XDEF	_CreatePort
	XDEF	_ConToWin
	XDEF	_IntputName
	XDEF	_ID

	ENDC


	SECTION TEXT0,CODE

__start_me_up	; int = _start_me_up( void)
	moveq	#ERROR_INVALID_RESIDENT_LIBRARY,D2	; Set up possible error

	movea.l	(4).w,A6			; Zero-Page addressing mode!
	cmpi.w	#32,LIB_VERSION(A6)		; At least OS 1.2; it contains
	bls.w	__exitReal			;  the OpenLibrary( name, version) call

	moveq	#ERROR_NO_FREE_STORE,D2		; Set up possible error

	qmove.l	BT_SIZEOF,D0			; Var-table size
	move.l	#MEMF_CLEAR|MEMF_PUBLIC,D1	; Prevent from being used by GIGAMEM etc.
	jsr	_LVOAllocMem(A6)
	tst.l	D0
	beq.w	__exitReal			; No BSS section

	movea.l	D0,A4				; Set up base register
	adda.l	#BREL,A4

	put.l	A6,_SysBase			; Remember it (if possible in fast-ram)

	suba.l	A1,A1
	jsr	_LVOFindTask(A6)
	put.l	D0,_ThisTask

	moveq	#ERROR_INVALID_RESIDENT_LIBRARY,D2	; Set up possible error

	lea	_DOSName(pc),A1
	moveq	#36,D0				; beta Kick 2.0 and up
	jsr	_LVOOpenLibrary(A6)
	put.l	D0,_DOSBase
	beq.w	__exitNoDOS

	lea	_IntName(pc),A1
	moveq	#36,D0
	jsr	_LVOOpenLibrary(A6)
	put.l	D0,_IntuitionBase
	beq.w	__exitDOSOpen

	lea	_WBenchName(pc),A1
	moveq	#36,D0
	jsr	_LVOOpenLibrary(A6)
	put.l	D0,_WBenchBase
	beq.w	__exitDOSOpen

	lea	_CxName(pc),A1
	moveq	#36,D0
	jsr	_LVOOpenLibrary(A6)
	put.l	D0,_CxBase
	beq.w	__exitDOSOpen

* ##############################

	lea	_ConName(pc),A0			; Get current console handle (from where we have been started)
	move.l	A0,D1
	move.l	#MODE_OLDFILE,D2		; It really exists...
	geta.l	_DOSBase,A6
	jsr	_LVOOpen(A6)			; Tell DOS we'll use console window
	put.l	D0,_ConHandle			; so it cannot be closed

* ##############################

*
** Copy from Shell given name into private buffer
*
	geta.l	_ThisTask,A0
	movea.l	pr_CLI(A0),A0			; Pointer CommandLineInterface
	add.l	A0,A0				; BPTR to APTR
	add.l	A0,A0
	movea.l	cli_CommandName(A0),A2		; Pointer to BSTR
	adda.l	A2,A2				; BSTR to STRPTR
	adda.l	A2,A2
	moveq	#0,D2				; Erase D2
	move.b	(A2)+,D2			; Get length-byte of name
	move.l	D2,D0				; Length of name also in D0
	addq.w	#8,D0				; Plus 1 because of zero-byte
	andi.l	#-8,D0	
	put.l	D0,__PgmNameSize		; Store in basetable
	move.l	#MEMF_PUBLIC|MEMF_CLEAR,D1	; Requirements
	geta.l	_SysBase,A6
	jsr	_LVOAllocMem(A6)		; Get memory
	put.l	D0,__PgmName			; Store in basetable
	beq.w	__exitNoPgmNameMem		; If no memory
	movea.l	D0,A0				; Buffer for name
	bra.s	2$				; 'Cause of DBF (-alias)
1$
	move.b	(A2)+,(A0)+			; Copy name
2$
	subq.w	#1,D2
	bcc.s	1$				; If some chars left
	clr.b	(A0)				; Zero-byte


*
** Here we check now whether the pgm is in the ResidentList; if it is we'll
** increment the UseCount (seg_UC) but the first code hunk is not erased in
** this case to break the hunk-list of the AmigaDOS load-file.
*
	lea	__start_me_up(pc),A0
	subq.l	#4,A0				; APTR segment
	bsr.w	_FindSegment			; Find own Resident-Segment
	put.l	D0,__ResSegment			; Resident-Segment or zero
	beq.s	.normalStart

	movea.l	D0,A0
	addq.l	#1,seg_UC(A0)			; We are resident, so the second code fragment is,
*						  but the first code segment quits thus we increment
*						  usecount by 1 indicating TSM (2nd code fragment)
*						  is still used!

.normalStart
	
*
** Now we start the following code-segment(s) (goes into D3) as real background process
*
	lea	__start_me_up(pc),A0		; Pointer to code start
	subq.l	#4,A0				; Pointer to BPTR next-segment
	move.l	(A0),D3				; BPTR-segment

	taz.l	__ResSegment			; Flag set (segment in ResList)?
	bne.s	.hunkStuffDone			; Yes...

	clr.l	(A0)				; Erase linked hunk-pointer to next segment (important!) (only if not resident!)

.hunkStuffDone
	get.l	__PgmName,D1			; Address name task
	moveq	#0,D2
	get.l	_ThisTask,A0			; Current TSM process
	move.b	LN_PRI(A0),D2			; Get current TSM's priority
	subq.b	#1,D2				; One less than normal priority (pri is non-important while running, but at startup!)
*	move.l	D3,D3				; Segment(s)
	move.l	#2560,D4			; Stack size (2.5 KByte)
	geta.l	_DOSBase,A6			; DOS takes control
	jsr	_LVOCreateProc(A6)		; Create a DOS process
	tst.l	D0				; D0 ~0 = msg-port of created task
	beq.w	__exitNoProc			; If zero, error!

	movea.l	D0,A0
	lea	-TC_SIZE(A0),A0			; From port to process
	move.l	A4,TC_Userdata(A0)		; Overgive new task the base-table

	move.l	#$10000,D0			; Bit 16
	geta.l	_SysBase,A6
	jsr	_LVOWait(A6)			; Wait for 2nd program to say we can quit

*
* The allocated/opened things will not freed nor closed !!!
*
	suba.l	A1,A1
	geta.l	_SysBase,A6
	jsr	_LVOFindTask(A6)
	movea.l	D0,A0				; ThisTask
	clr.l	pr_Result2(A0)			; No error!
	moveq	#0,D0				; For the CLI
	rts					; Program (#1) ends

*
** This routine will test if in the ResidentList of DOS the overgiven segment
** resides.
*
_FindSegment	; struct Segment *FindSegment( struct *Segment)
*			D0			     A0
	move.l	A2,-(sp)

	geta.l	_DOSBase,A1
	movea.l	dl_Root(A1),A1			; RootNode
	movea.l	rn_Info(A1),A1			; BPTR DosInfo
	adda.l	A1,A1
	adda.l	A1,A1				; DosInfo
	tst.l	di_NetHand(A1)
	beq.s	.SegNotFound
	movea.l	di_NetHand(A1),A1		; BPTR NetHand
	adda.l	A1,A1
	adda.l	A1,A1				; NetHand (pointer to ResList)
	bra.s	.CmpSeg
.NextSeg
	tst.l	seg_Next(A1)
	beq.s	.SegNotFound
	movea.l	seg_Next(A1),A1			; BPTR segment
	adda.l	A1,A1
	adda.l	A1,A1				; APTR segment
.CmpSeg
	movea.l	seg_Seg(A1),A2			; BPTR segment
	adda.l	A2,A2
	adda.l	A2,A2				; APTR segment

	cmpa.l	A0,A2				; Segments the same?
	bne.s	.NextSeg

	move.l	A1,D0				; ResList
.EndSeg
	movea.l	(sp)+,A2
	rts

.SegNotFound
	moveq	#0,D0
	bra.s	.EndSeg

* ################################

__exitNoProc
	moveq	#ERROR_TASK_TABLE_FULL,D2

	geta.l	__PgmName,A1
	get.l	__PgmNameSize,D0
	geta.l	_SysBase,A6
	jsr	_LVOFreeMem(A6)

__exitNoPgmNameMem
	get.l	_ConHandle,D1
	geta.l	_DOSBase,A6
	jsr	_LVOClose(A6)			; Allow to shut down console

__exitDOSOpen
	geta.l	_SysBase,A6

	taz.l	_WBenchBase
	beq.s	1$
	geta.l	_WBenchBase,A1
	jsr	_LVOCloseLibrary(A6)
1$
	taz.l	_CxBase
	beq.s	1$
	geta.l	_CxBase,A1
	jsr	_LVOCloseLibrary(A6)

	taz.l	_IntuitionBase
	beq.s	2$
	geta.l	_IntuitionBase,A1
	jsr	_LVOCloseLibrary(A6)
2$
	geta.l	_DOSBase,A1
	jsr	_LVOCloseLibrary(A6)

__exitNoDOS
	geta.l	_ThisTask,A0
	move.l	D2,pr_Result2(A0)

	movea.l	A4,A1
	suba.l	#BREL,A1
	qmove.l	BT_SIZEOF,D0
	jsr	_LVOFreeMem(A6)

__exitReal
	move.l	D2,D0
	rts

_DOSName
	dc.b	'dos.library',0
_IntName
	dc.b	'intuition.library',0
_WBenchName
	dc.b	'workbench.library',0
_CxName
	dc.b	'commodities.library',0
_ConName
	dc.b	'CONSOLE:',0				; <- means current input stream


* ################################################################

	SECTION	TEXT1,CODE

__createStartup
	movea.l	(4).w,A6			; SysBase alias
	suba.l	A1,A1
	jsr	_LVOFindTask(A6)
	movea.l	D0,A0				; Own task
.getBaseReg
	tst.l	TC_Userdata(A0)
	beq.s	.getBaseReg
	movea.l	TC_Userdata(A0),A4		; Set up base register
	get.l	_ThisTask,-(sp)			; Master task
	put.l	A0,_ThisTask			; And setup new own task !!!

	get.l	_ConHandle,D0			; Console's handle
	adr	_WinPtr,A0
	adr	_ConTask,A1
	adr	_ConPort,A2
	bsr.w	_ConToWin			; Get console's window, task and port address

	movea.l	(sp)+,A1
	move.l	#$10000,D0			; Signal bit 16
	geta.l	_SysBase,A6
	jsr	_LVOSignal(A6)			; Tell master task to quit

	moveq	#10,D1
	put.l	D1,_errno			; Set up possible error

	tst.l	D0				; Do we got the needed things from ConToWin()?
	beq.w	3$

	bsr.w	_CreatePort
	put.l	D0,_AppPort
	beq.s	3$

	bsr.w	_CreatePort
	put.l	D0,_ReplyPort
	beq.s	2$

	adr	_InputIOReq,A1
	moveq	#IOF_QUICK,D0
	move.b	D0,IO_FLAGS(A1)
	lea	_InputName(pc),A0		; We'll use
	moveq	#0,D0				;  the
*	adr	_InputIOReq,A1			;  console
	moveq	#0,D1				;  device
	jsr	_LVOOpenDevice(A6)		;  to send RAW-KEY
	tst.l	D0				;  codes
	bne.s	1$

	adr	_InputIOReq,A0			; Set reply-port
	get.l	_ReplyPort,MN_REPLYPORT(A0)	; so that the console-device knows who gets the msgs

	moveq	#0,D0
	moveq	#0,D1
	geta.l	_WinPtr,A0
	geta.l	_AppPort,A1
	suba.l	A2,A2
	geta.l	_WBenchBase,A6
	jsr	_LVOAddAppWindowA(A6)		; Make out of the console window (Shell) an app-windoww
	put.l	D0,_AppWin
	beq.s	0$				; No App-Window thus do nada

	moveq	#0,D0
	put.l	D0,_errno			; No error occurred

	bsr.w	_AwaitCloseDown			; Fetch datas from console's window

	geta.l	_AppWin,A0
	geta.l	_WBenchBase,A6
	jsr	_LVORemoveAppWindow(A6)

0$
	adr	_InputIOReq,A1
	geta.l	_SysBase,A6
	jsr	_LVOCloseDevice(A6)	

1$
	geta.l	_ReplyPort,A0
	bsr.w	_RemPort

2$
	geta.l	_AppPort,A0
	bsr.w	_RemPort

3$
	get.l	_ConHandle,D1
	geta.l	_DOSBase,A6
	jsr	_LVOClose(A6)			; Allow to shut down console

	get.l	_errno,D2
	geta.l	_ThisTask,A0
	move.l	D2,pr_Result2(A0)

	geta.l	_SysBase,A6
	jsr	_LVOForbid(A6)

	taz.l	__ResSegment			; ResList?
	beq.s	4$
	geta.l	__ResSegment,A0
	subq.l	#1,seg_UC(A0)			; Usecount -1!
	bra.s	5$
4$
	lea	__createStartup(pc),A0		; Pointer to pgm code
	subq.l	#4,A0				; -4 = segment pointer
	move.l	A0,D1				; to D1
	lsr.l	#2,D1				; to BPTR
	geta.l	_DOSBase,A6			; Base DOS
	jsr	_LVOUnLoadSeg(A6)		; Remove by pgm used mem
5$
	geta.l	_SysBase,A6

	geta.l	_CxBase,A1
	jsr	_LVOCloseLibrary(A6)

	geta.l	_WBenchBase,A1
	jsr	_LVOCloseLibrary(A6)

	geta.l	_IntuitionBase,A1
	jsr	_LVOCloseLibrary(A6)

	geta.l	_DOSBase,A1
	jsr	_LVOCloseLibrary(A6)

	geta.l	__PgmName,A1
	get.l	__PgmNameSize,D0
	jsr	_LVOFreeMem(A6)

	movea.l	A4,A1
	suba.l	#BREL,A1
	qmove.l	BT_SIZEOF,D0
	jsr	_LVOFreeMem(A6)

	jsr	_LVOPermit(A6)

	move.l	D2,D0
	rts

* ################### MAIN ###################

_AwaitCloseDown
	move.l	A5,-(sp)

	geta.l	_ConTask,A5			; Let us talk with the console task

	tst.l	TC_Userdata(A5)			; Assumption: if TC_Userdata is used, another TSM obtains
	bne.s	.done				; the handle to this console task so we should terminate immediately

	bsr.w	_CreatePort
	put.l	D0,_MsgPort			; We need a message port...
	beq.s	.done

	move.l	A4,TC_Userdata(A5)		; Remember A4 in global for new WaitPkt() function
	put.l	pr_MsgPort(A5),_OldPort		; Remember for restoring the old msg-port of console task
	move.l	D0,pr_MsgPort(A5)		; Drop our message port where old of console task has been hooked up

	lea	_NewPktWait(pc),A0		; The additional function for the original WaitPkt()
	move.l	A0,pr_PktWait(A5)

	geta.l	_SysBase,A6

.event
	geta.l	_AppPort,A0
	moveq	#1,D0
	moveq	#0,D1
	move.b	MP_SIGBIT(A0),D1
	lsl.l	D1,D0
	ori.w	#$1000,D0			; And also CTRL-C (bit 12)
	jsr	_LVOWait(A6)			; Wait...

	moveq	#12,D1				; Does our task (not the console's one) got a break signal?
	btst.l	D1,D0
	bne.s	.remove

	geta.l	_AppPort,A0			; Check if we got a workbench message
	moveq	#0,D1
	move.b	MP_SIGBIT(A0),D1		; Signal bit of port
	btst.l	D1,D0
	bne.s	.receivedAppMsg			; Message received...

	bra.s	.event				; If not...

.remove
	clr.l	pr_PktWait(A5)			; Remove additional function for WaitPkt()
	get.l	_OldPort,pr_MsgPort(A5)		; Restore original (by DOS set up) msg-port
	clr.l	TC_Userdata(A5)			; Restore
	geta.l	_MsgPort,A0			; Our allocated msg-port
	bsr.w	_RemPort			; Remove it
.done
	movea.l	(sp)+,A5
	rts

.receivedAppMsg
	geta.l	_AppPort,A0
	jsr	_LVOGetMsg(A6)
	tst.l	D0
	beq.s	.msgLoopDone

	move.l	D0,-(sp)

	movea.l	D0,A0
	bsr.s	_ReactToMsg

	movea.l	(sp)+,A1
	jsr	_LVOReplyMsg(A6)

.msgLoopDone
	bra.s	.event

	IFND	MTYPE_APPWINDOW			; Define of include file V39 to V40 differ...
MTYPE_APPWINDOW	EQU	7
	ENDC

_ReactToMsg
	movem.l	D2-D4/A2/A6,-(sp)

	cmpi.w	#MTYPE_APPWINDOW,am_Type(A0)
	bne.s	5$

	movea.l	A0,A2
	moveq	#1,D4

	geta.l	_WinPtr,A0
	geta.l	_IntuitionBase,A6
	jsr	_LVOActivateWindow(A6)		; Which window gets the input stream?

	geta.l	_DOSBase,A6
1$
	move.l	D4,D0				; Parse argument x
	subq.w	#1,D0				; as index
	lsl.w	#3,D0				; * 8
	movea.l	am_ArgList(A2),A0		; Arguments
	lea	0(A0,D0.w),A0			; Current argument
	move.l	A0,D0
	beq.s	5$				; E.g.: Disk is mounted (e.g. CD-ROM) but not in drive!

	move.l	A0,-(sp)			; Current arg-pointer onto stack

	move.l	wa_Lock(A0),D1			; Get lock of drawer
	adr	_StrStorage,A0			; Buffer to store characters
	move.w	#$2022,(A0)+			; Write blank and double quote
	move.l	A0,D2
	qmove.l	251,D3				; Max. room for 251 characters
	jsr	_LVONameFromLock(A6)		; Get name from lock

	adr	_StrStorage,A0			; Address string buffer
2$
	tst.b	(A0)+				; Find end of drawer name
	bne.s	2$

	subq.l	#1,A0				; One too far; pointed to zero byte (terminator)
	cmpi.b	#':',-1(A0)			; Volume name or drawer name?
	beq.s	3$

	move.b	#'/',(A0)+			; Was drawer...
3$
	move.l	(sp)+,A1			; Get back pointer to arg-pointer
	movea.l	wa_Name(A1),A1			; Name of file
4$
	move.b	(A1)+,(A0)+			; Copy name at end of drawer name
	bne.s	4$

	move.b	#'"',-1(A0)
	clr.b	(A0)

	adr	_StrStorage,A0			; Address complete filename
	bsr.s	_WriteIE			; Write RAW-KEY-codes

	addq.l	#1,D4				; One more argument
	cmp.l	am_NumArgs(A2),D4
	bls.s	1$				; If some left to work out

5$
	movem.l	(sp)+,D2-D4/A2/A6
	rts

_WriteIE	; STRPTR A0
	move.l	A2,-(sp)
	move.l	A6,-(sp)

	movea.l	A0,A2				; The string

.writeIE
	tst.b	(A2)
	beq.s	.ieDone

	adr	_InputIOReq,A1
	moveq	#IOF_QUICK,D0
	move.b	D0,IO_FLAGS(A1)
	moveq	#IND_WRITEEVENT,D0
	move.w	D0,IO_COMMAND(A1)
	moveq	#ie_SIZEOF,D0
	move.l	D0,IO_LENGTH(A1)
	adr	_InputEvent,A0
	move.l	A0,IO_DATA(A1)

	moveq	#IECLASS_RAWKEY,D0
	move.b	D0,ie_Class(A0)
	moveq	#0,D0
	move.w	D0,ie_Code(A0)
	move.b	D0,ie_SubClass(A0)
	lea	ie_TimeStamp(A0),A1
	move.l	D0,TV_SECS(A1)
	move.l	D0,TV_MICRO(A1)

	movea.l	D0,A1				; Zero !!!
	move.b	(A2)+,D0			; Character
	geta.l	_CxBase,A6
	jsr	_LVOInvertKeyMap(A6)		; Convert ASCII to RAW-KEY code
	tst.l	D0
	beq.s	.writeIE
	
	adr	_InputIOReq,A1
	geta.l	_SysBase,A6
	jsr	_LVODoIO(A6)			; Send RAW-KEY-codes to input stream
	bra.s	.writeIE
	
.ieDone
	movea.l	(sp)+,A6
	movea.l	(sp)+,A2
	rts

* ############### ADDITIONAL TO PktWait() ##################

_NewPktWait
	movem.l	D2/A0-A1/A4/A6,-(sp)

	movea.l	(4).w,A6			; Already set but...
	suba.l	A1,A1
	jsr	_LVOFindTask(A6)		; We're running from the CON-task
	movea.l	D0,A0				; Con-task
	movea.l	TC_Userdata(A0),A4		; Get base-register (stored in user-data)
*
** We want to talk to the process of the console window
*
	lea	pr_MsgPort(A0),A0		; Console process msg-port
	move.l	A0,D2
	beq.s	.exitFalse			; If no msg-port...

.getPkt
	movea.l	D2,A0				; Messasge port
	jsr	_LVOGetMsg(A6)			; Get message
	tst.l	D0
	beq.s	.waitPkt			; If none

	move.l	D0,D2				; Remember in D2 (message for console process)

*
** We do not only check for ACTION_END, we care also about any REXX-server and IXEMUL-application
** who are using DOS-packets on their own; thus we need an identification for CONSOLE's termination,
** which infact is indicated through a Result1 of #-1 (TRUE) !!!
** NOTE: Earlier systems than v39 may use #1 for TRUE which is also o.k. .
*
	movea.l	D0,A0
	movea.l	LN_NAME(A0),A0			; WaitPkt() modifies a message so that it is stored in LN_NAME
	cmpi.l	#ACTION_END,dp_Type(A0)		; User/application want to close something?
	bne.s	.return

	cmpi.l	#1,dp_Res1(A0)			; Old synonym fro TRUE?
	beq.s	.sayQuit

	cmpi.l	#-1,dp_Res1(A0)			; Really quit?
	bne.s	.return

.sayQuit
	geta.l	_ThisTask,A1			; Main (TSM) process (not console)!
	move.l	#$1000,D0			; Break signal (bit 12)
	jsr	_LVOSignal(A6)			; Awake main process (tell it to terminate)

.return
	move.l	D2,D0				; Message

	movem.l	(sp)+,D2/A0-A1/A4/A6
	rts

.waitPkt
	moveq	#1,D0
	movea.l	D2,A0				; Msg-port
	moveq	#0,D1
	move.b	MP_SIGBIT(A0),D1		; Get signal bit of message port
	lsl.l	D1,D0				; 1 << 31?	(normally bit 31 is the first free signal)
	ori.w	#$100,D0			; Wait also for packet arrival (bit 8)
	jsr	_LVOWait(A6)
	bra.s	.getPkt

.exitFalse
	adr	_DummyMessage,A0		; Because the function NewPktWait() is a sub of PktWait()
	move.l	A0,D0				; and PktWait() doesn't deals with empty messages we have
*						  to create a dummy dos-packet where PktWait() can modify
*						  it

*	ROM-listing of PktWait(): [OS 1.1 through OS 3.1]
*
*	d_PktWait	equ	pr_PktWait-pr_MsgPort
*
*	_PktWait	; struct StandardPacket *PktWait( void);
*		movem.l	D7/A0-A1/A6,-(sp)
*		movea.l	(4).w,A6
*		movea.l	ThisTask(A6),A0		Own Task/process structure
*		cmpi.b	#NT_PROCESS,LN_TYPE(A0)
*		bne.s	.NotAProcess
*		adda.w	#pr_MsgPort,A0		From Process to msg-port
*		move.l	d_PktWait(A0),D1	Get installed PktWait() function of process structure
*		beq.s	.NoPacketWait
*		movea.l	D1,A0
*		jsr	(A0)			Calls installed PktWait() function
*		bra.s	.ModifyMsg
*	.NotAProcess
*		movea.l	A1,A0			If we're are an exec-task, call: struct StandardPacket *PktWait( struct MsgPort *p);
*	.NoPacketWait
*		move.l	A0,D7
*	.GetMsg
*		movea.l	D7,A0
*		jsr	_LVOGetMsg(A6)
*		tst.l	D0
*		beq.s	.WaitForMsg
*	.ModifyMsg				This is it, no empty messages allowed!!!
*		movea.l	D0,A0
*		move.l	LN_NAME(A0),D0		Packet
*		move.l	D0,D1
*		asr.l	#2,D1			BPTR Packet
*		movem.l	(sp)+,D7/A0-A1/A6
*		rts
*	.WaitForMsg
*		moveq	#1,D0
*		rol.l	#8,D0
*		jsr	_LVOWait(A6)
*		bra.s	.GetMsg


	movem.l	(sp)+,D2/A0-A1/A4/A6
	rts

* ##################################

_RemPort	; A0 - Port
	move.l	A6,-(sp)

	geta.l	_SysBase,A6

	move.b	MP_SIGBIT(A0),D0
	move.l	A0,-(sp)
	jsr	_LVOFreeSignal(A6)

	movea.l	(sp)+,A1
	moveq	#MP_SIZE,D0
	jsr	_LVOFreeMem(A6)

	movea.l	(sp)+,A6
	rts

_CreatePort	; void
	move.l	A6,-(sp)
	move.l	A2,-(sp)

	geta.l	_SysBase,A6

	moveq	#MP_SIZE,D0
	move.l	#MEMF_CLEAR|MEMF_PUBLIC,D1	; Prevent from being used by virtual memory
	jsr	_LVOAllocMem(A6)
	tst.l	D0
	beq.s	1$
	movea.l	D0,A2

	moveq	#-1,D0				; Alloc _ANY_ signal
	jsr	_LVOAllocSignal(A6)
	cmpi.l	#-1,D0
	beq.s	2$
	move.b	D0,MP_SIGBIT(A2)

	suba.l	A1,A1
	jsr	_LVOFindTask(A6)
	move.l	D0,MP_SIGTASK(A2)
	move.b	#NT_MSGPORT,LN_TYPE(A2)
	lea	MP_MSGLIST(A2),A0
	NEWLIST A0				; Exec include file MACRO

	move.l	A2,D0

	movea.l	(sp)+,A2
	movea.l	(sp)+,A6
	rts

2$
	movea.l	A2,A1
	moveq	#MP_SIZE,D0
	jsr	_LVOFreeMem(A6)
1$
	movea.l	(sp)+,A2
	movea.l	(sp)+,A6

	moveq	#0,D0
	rts

* ##################################

*
** This material was released by Commodore Amiga (with stuff dropped in amiga.lib).
** Jan Kautz published a shortened version in a disk-mag (written in C).
** I translated it to asssembler without using any stuff of the amiga.lib.
*
_ConToWin	; (BPTR consoleHandle, struct Window **win, struct Process **proc, struct MsgPort **msgPort)
*			D0			A0			A1			A2

	movem.l	D2-D5/A2-A6,-(sp)

	move.l	A0,D3				; Variable window
	move.l	A1,D4				; Variable contask
	move.l	A2,D5				; Variable conport

	move.l	D0,D2				; Console Handle

	bsr.s	_CreatePort			; We need a port to comminicate with DOS
	movea.l	D0,A5				; IOReplyPort
	tst.l	D0
	beq.w	1$

	moveq	#id_SIZEOF,D0
	move.l	#MEMF_CLEAR|MEMF_PUBLIC,D1
	geta.l	_SysBase,A6
	jsr	_LVOAllocMem(A6)		; The InfoData-structure holds the result after
	movea.l	D0,A3				; the DOS-packet has been invoked (InfoData)
	tst.l	D0
	beq.w	2$

	moveq	#sp_SIZEOF,D0
	move.l	#MEMF_CLEAR|MEMF_PUBLIC,D1
	jsr	_LVOAllocMem(A6)		; Let's talk to DOS via a packet
	movea.l	D0,A2				; StandardPacket
	tst.l	D0
	beq.s	3$

*	movea.l	A2,A2				; Packet
	lea	sp_Pkt(A2),A0			; Pointer to sp_Pkt
	move.l	A0,LN_NAME(A2)			; sp_Msg ^ node ^ name (valid DOS-packet)
	move.l	A0,dp_Link(A0)			; sp_Pkt ^ dp_Link
	move.l	A5,dp_Port(A0)			; sp_Pkt ^ dp_Port (IOReplyPort)
	moveq	#ACTION_DISK_INFO,D0
	move.l	D0,dp_Type(A0)			; sp_Pkt ^ dp_Type
	move.l	A3,D0				; APTR InfoData
	lsr.l	#2,D0				; to BPTR
	move.l	D0,dp_Arg1(A0)			; sp_Pkt ^ dp_Arg1

	movea.l	D2,A0				; File handle (BPTR)
	adda.l	A0,A0
	adda.l	A0,A0				; APTR file handle
	movea.l	fh_Type(A0),A0			; Console port to put msg
	movea.l	MP_SIGTASK(A0),A0		; Console's task
	movea.l	D4,A1
	move.l	A0,(A1)				; Save pointer to ConTask
	lea	pr_MsgPort(A0),A0		; Task's port
	movea.l	D5,A1
	move.l	A0,(A1)				; Save pointer to ConPort
	movea.l	A2,A1				; Packet
	jsr	_LVOPutMsg(A6)

	movea.l	A5,A0				; IOReplyPort
	jsr	_LVOWaitPort(A6)

	movea.l	D3,A0
	move.l	id_VolumeNode(A3),(A0)		; InfoData ^ VolumeNode = window's address

	movea.l	A2,A1				; StandardPacket
	moveq	#sp_SIZEOF,D0
	jsr	_LVOFreeMem(A6)

	movea.l	A3,A1				; InfoData
	moveq	#id_SIZEOF,D0
	jsr	_LVOFreeMem(A6)

	movea.l	A5,A0				; IOReplyPort
	bsr.w	_RemPort

	moveq	#-1,D0				; TRUE
	movem.l	(sp)+,D2-D5/A2-A6
	rts

3$
	movea.l	A3,A1				; InfoData
	moveq	#id_SIZEOF,D0
	jsr	_LVOFreeMem(A6)
2$
	movea.l	A5,A0				; IOReplyPort
	bsr.w	_RemPort
1$
	moveq	#0,D0				; FALSE
	movem.l	(sp)+,D2-D5/A2-A6
	rts

_InputName
	dc.b	'input.device',0
_ID
	dc.b	'$VER: Tame Shell Master 1.2 (13.11.99) © 1999 »ONIX« - all rights reserved',13,10,0

	END
