*------------------------------------------------------------------------------
*
*  k.asm - a key macro utility.
*  
*------------------------------------------------------------------------------

	Section	KeyCode,code

******* Included Files *************************************************

	INCLUDE "keys.i"


******* Imported *******************************************************

	xref	_SysBase
	xref	_DOSBase
	xref	_IntuitionBase	
	xref	_exit

	xref	NWPointer		; pointer to NewWindow structure.
	xref	FreePort

	xref	_bopen
	xref	_bclose
	xref	_bread
	xref	_bwrite

******* Exported *******************************************************

	xdef	IHandler	
	xdef	IntRec
	xdef	ConIOBlock
	xdef	InpIOBlock
	xdef	TheTitle
	xdef	DefinedKeys
	xdef	StringBuf
	xdef	AddKey
	xdef	FileReadChar
	xdef	_main



*------------------------------------------------------------------------------
_main
	;------ Get ready to process input
	bsr	SetupStuff

0$	move.l	OurSigMask(pc),d0
	ExecF	Wait

	;------ We got a signal - Do what we came here for...
	;------ If Event is null, wait some more. If it is DOCMD, open
	;------ the command window. Any other value is considered to be
	;------ a process macro command.
	tst.b	Event
	beq.s	0$

	cmp.b	#DOCMD,Event
	beq.s	2$

1$	;------ At this point, we have a macro to process. Check the Q
	;------ for the next one.
	bsr	DeQ
	beq.s	0$
	
	lea	kr_MacStr(a2),a2
	bsr	InsertString
	bra.s	1$			; Check for another macro.

2$	;------ Set the current directory to the Initial CLI's.
	;------ This has been taken out because this prog crashes
	;------ violently if the Initial CLI no longer exists...
	;	bsr	SetDirectory

	;------ Get the ptr to the current (frontmost) screen
	;------ courtesy of intuitionbase.
	move.l	_IntuitionBase(pc),a0
	move.l	ib_FirstScreen(a0),a1

	;------ We now have a pointer to the screen structure in a1.
	;------ Open a window onto this screen.	
	bsr	OpenTheWindow

	;------ Open the console device on this window:
	bsr	OpenCon

	;------ Read Choice and process it
	bsr	ProcessChoice

	;------ All done - close the console device and window.
	bsr	CloseAll
	tst.b	KillFlag
	beq.s	0$
	rts


*------------------------------------------------------------------------------
SetDirectory
	;------ Set our directory to the Initial CLI's, so we can find 
	;------ stuff.

	lea	CLIName(pc),a1
	ExecF	FindTask

	move.l	d0,a0
	move.l	pr_CurrentDir(a0),d1
	DosF	CurrentDir
	rts

*------------------------------------------------------------------------------
ProcessChoice
	move.l	sp,ErrorSp

0$	;------ Prompt for a menu choice and then execute it.
	lea	ChoicePrompt(pc),a0
	bsr	PutPrompt

	bsr	ConReadChar		; get key...
	move.b	CharBuf,d7

	;------ Escape will terminate the window.
	cmp.b	#27,d7
	bne.s	1$
	rts

1$	bsr	ConWriteChar		; ...echo character
	bsr	ConReadChar		; ...get another

	;------ Do something with the entered choice.
	Ucase	d7
	
	cmp.b	#'K',d7
	seq	KillFlag
	bne.s	2$
	rts

2$	cmp.b	#'R',d7
	bne.s	4$
	bsr	RemoveKey
	bra.s	0$

4$	cmp.b	#'D',d7
	bne.s	6$
	bsr	DefineKey
	bra.s	0$

6$	cmp.b	#'S',d7
	bne.s	8$
	bsr	SaveKeys
	bra.s	0$
	
8$	cmp.b	#'L',d7
	bne.s	10$
	bsr	LoadKeys
	bra.s	0$
	
10$	cmp.b	#'V',d7
	bne.s	12$
	bsr	ViewKeys

12$	bra.s	0$		; Bad choice, get another one.


*------------------------------------------------------------------------------
DefineKey
	;------ Prompt for the key to define, and then define it!
	lea	DefinePrompt(pc),a0
	bsr	PutPrompt
	
	lea	StringBuf(pc),a2
	bsr	ConReadLine
	
	bsr	AddKey
	move.l	a0,d7			; Any mem free?
	bne.s	0$			; Nope, don't do nothin

	UhOh	ErNoMem			; Print no mem msg.

0$	lea	MacPrompt(pc),a0
	bsr	PutPrompt

	;------ Get the key's definition
	move.l	d7,a0
	lea	kr_MacStr(a0),a2
	bsr	ConReadLine
	rts
	

*------------------------------------------------------------------------------
RemoveKey
	;------ Prompt for the key to remove.
	lea	RemovePrompt(pc),a0
	bsr	PutPrompt
	
	;------ Get key description.
	lea	StringBuf(pc),a2
	bsr	ConReadLine
	
	;------ Convert description to Qualifier/Code form.
	lea	StringBuf(pc),a2
	bsr	ConvertCharacter

	swap	d0
	move.w	d1,d0
	swap	d0
	bsr	LookForKey
	bne.s	2$

	UhOh	ErNoKey

2$	;------ Remove the key from the list.
	bsr	DelKey
	rts
	

*------------------------------------------------------------------------------
LoadKeys
	lea	LoadPrompt(pc),a0
	bsr	PutPrompt
	lea	StringBuf(pc),a2
	bsr	ConReadLine

	;------ Attempt to open the file
	move.l	#StringBuf,d1
	move.l	#MODE_OLDFILE,d2
	move.l	#2048,d3
	jsr	_bopen
	move.l	d0,d4
	bne.s	0$

	UhOh	ErNoFile
	
0$	lea	LoadingMsg(pc),a0
	bsr	PutWindowMsg

2$	move.b	#' ',d5
	lea	StringBuf(pc),a2
	bsr	FileReadItem
	beq.s	4$

	bsr	AddKey
	cmpa.l	#0,a0
	bne.s	3$

	move.l	d4,d1
	jsr	_bclose
	UhOh	ErNoMem
	
3$	move.b	#10,d5
	lea	kr_MacStr(a0),a2
	bsr	FileReadItem
	bra.s	2$

4$	move.l	d4,d1
	jsr	_bclose
	rts



*------------------------------------------------------------------------------
SaveKeys	
	lea	SavePrompt(pc),a0
	bsr	PutPrompt
	lea	StringBuf(pc),a2
	bsr	ConReadLine
	
	;------ Attempt to open the file
	move.l	#StringBuf,d1
	move.l	#MODE_NEWFILE,d2
	move.l	#2048,d3
	jsr	_bopen
	move.l	d0,d4
	bne.s	0$

	UhOh	ErNoFile
	
0$	lea	SavingMsg(pc),a0
	bsr	PutWindowMsg

	lea	DefinedKeys(pc),a2

2$	move.l	kr_Next(a2),a2
	cmp.l	#0,a2
	beq.s	4$
	
	;------ Write the key description.
	lea	kr_KeyStr(a2),a3
	move.b	#' ',CharBuf
	bsr	FileWriteItem
	
	;------ Write the key macro string.
	lea	kr_MacStr(a2),a3
	move.b	#10,CharBuf
	bsr	FileWriteItem
	bra.s	2$

4$	move.l	d4,d1
	jsr	_bclose
	rts



*------------------------------------------------------------------------------
ViewKeys	
	ClearWindow
	lea	ViewPrompt(pc),a0
	bsr	PutWindowMsg

	;------ Resize the window
	move.l	TheWindow(pc),a0
	clr.l	d0
	move.l	#178,d1
	IntF	SizeWindow

	;------ Create an input event so intuition will resize the window
	move.w	#128,d0
	clr.w	d1
	bsr	InsertEvent

	;------ Print the macros
	lea	DefinedKeys(pc),a2

2$	move.l	kr_Next(a2),a2
	cmp.l	#0,a2
	beq.s	4$
	
	;------ Write the key description.
	lea	kr_KeyStr(a2),a0
	bsr	ConWriteLine
	move.b	#' ',CharBuf
	bsr	ConWriteChar
	
	;------ Write the key macro string.
	lea	kr_MacStr(a2),a0
	bsr	ConWriteLine
	lea	CrLf(pc),a0
	bsr	ConWriteLine
	bra.s	2$

4$	bsr	ConReadChar

	;------ Resize the window again
	move.l	TheWindow(pc),a0
	clr.l	d0
	move.l	#-178,d1
	IntF	SizeWindow
	rts


*------------------------------------------------------------------------------
AddKey
	;------ Allocate a structure for a key macro, and link it to the
	;------ list. Return its address in a0.
	
	move.l	#kr_SIZEOF,d0
	move.l	#(MEMF_PUBLIC!MEMF_CLEAR!MEMF_CHIP),d1
	ExecF	AllocMem
	tst.l	d0
	bne.s	0$
	
	suba.l	a0,a0
	rts

0$	;------ Add the record to the front of the list
	lea	DefinedKeys(pc),a0
	move.l	d0,a1
	
	move.l	kr_Next(a0),kr_Next(a1)
	move.l	a1,kr_Next(a0)
	move.l	a0,kr_Prev(a1)
	
	tst.l	kr_Next(a1)
	beq.s	2$
	
	move.l	kr_Next(a1),a0
	move.l	a1,kr_Prev(a0)

2$	;------ Place the defined key in the record
	lea	StringBuf(pc),a2
	bsr	ConvertCharacter
	
	move.w	d1,kr_Qual(a1)
	move.w	d0,kr_Key(a1)
	
	lea	StringBuf(pc),a2
	lea	kr_KeyStr(a1),a0
	
4$	move.b	(a2)+,(a0)+
	bne.s	4$

	move.l	a1,a0
	rts


*------------------------------------------------------------------------------
DelKey
	;------ Remove the key rec specified by a0 from the key list.
	move.l	kr_Prev(a0),a1
	move.l	kr_Next(a0),a2

	move.l	kr_Next(a0),kr_Next(a1)
	cmp.l	#0,a2
	beq.s	2$

	move.l	a1,kr_Prev(a2)

2$	move.l	a0,a1
	move.l	#kr_SIZEOF,d0	
	ExecF	FreeMem
	rts
	

*------------------------------------------------------------------------------
LookForKey
	;------ Search for the key specified by d0 in the DefinedKey list.
	;------ If the key was found, its address will be in a0.
	lea	DefinedKeys,a0

0$	move.l	kr_Next(a0),a0

	cmpa.l	#0,a0
	beq.s	2$

	cmp.l	kr_Qual(a0),d0
	bne.s	0$
	
	moveq	#1,d0
2$	rts


*------------------------------------------------------------------------------
FileReadItem
	;------ This routine will read either the key def or the macro
	;------ def from the file depending on the delimiter.

	clr.l	d6		; Keep a character count.

0$	bsr	FileReadChar
	tst.l	d0
	beq.s	4$

	cmp.b	#76,d6
	blt.s	1$

	move.l	d4,d1
	jsr	_bclose
	UhOh	ErDefTooLong

1$	cmp.b	(a2),d5
	beq.s	2$

	addq.l	#1,a2
	addq.l	#1,d6
	bra.s	0$
	
2$	clr.b	(a2)
	moveq	#1,d0
4$	rts


*------------------------------------------------------------------------------
FileReadChar
	move.l	d4,d1
	move.l	a2,d2
	moveq.l	#1,d3
	jsr	_bread
	rts


*------------------------------------------------------------------------------
FileWriteItem
	;------ Write the item to the file, and the terminator in CharBuf.
	;------ a3 points to the item to write.
	
0$	bsr	FileWriteChar
	
	addq.l	#1,a3
	tst.b	(a3)
	bne.s	0$
	
	;------ Write the terminator to the file.
	lea	CharBuf(pc),a3
	bsr	FileWriteChar
	rts


*------------------------------------------------------------------------------
FileWriteChar
	;------ Write the character pointed to by a3 to the file (d4).
	move.l	d4,d1
	move.l	a3,d2
	moveq.l	#1,d3
	jsr	_bwrite
	rts

*------------------------------------------------------------------------------
PutPrompt
	;------ Put a prompt in the title line, clear the window and 
	;------ place a prompt character in it. The desired prompt
	;------ is in (a0).

	bsr	PutWindowMsg

	ClearWindow

	lea	PromptStr(pc),a0
	bsr	ConWriteLine
	rts

	
*------------------------------------------------------------------------------
PutWindowMsg
	;------ Put a message in the window's title line. The message consists
	;------ of two parts: The window's title, and the specified message.
	lea	StringBuf(pc),a2
	lea	TheTitle(pc),a1
	
0$	move.b	(a1)+,(a2)+
	bne.s	0$

	lea	-1(a2),a2
2$	move.b	(a0)+,(a2)+
	bne.s	2$

	;------ Now print the prompt.
	lea	StringBuf(pc),a1
	move.l	#-1,a2
	move.l	TheWindow(pc),a0
	IntF	SetWindowTitles
	rts	

*------------------------------------------------------------------------------
ProcessError
	bsr	PutWindowMsg
	bsr	ConReadChar
	move.l	ErrorSp(pc),sp
	bra	ProcessChoice	
	rts



*------------------------------------------------------------------------------
EnQ
	ExecF	Forbid

	cmp.w	#64,QC			; Full yet?
	beq.s	0$

	lea	CmdQ(pc),a1

	addq.b	#4,QB+1
	move.w	QB(pc),d0
	move.l	a3,0(a1,d0.w)
	addq.w	#1,QC

0$	ExecF	Permit
	rts

*------------------------------------------------------------------------------
DeQ
	ExecF	Forbid

	tst.w	QC
	sne.b	d2
	beq.s	0$

	lea	CmdQ(pc),a1

	addq.b	#4,QF+1
	move.w	QF,d0

	move.l	0(a1,d0.w),a2
	subq.w	#1,QC

0$	ExecF	Permit
	tst.b	d2
	rts


*------------------------------------------------------------------------------
*
*  This section contains initialization and termination routines.
*  
*------------------------------------------------------------------------------


*------------------------------------------------------------------------------
SetupStuff
	;------ Perform the required initialization for console and 
	;------ input device I/O.

	;------ Initialize the queue.
	move.w	#252,QF
	move.w	#252,QB
	clr.w	QC

	;------ Fetch our task pointer
	move.l	_SysBase(pc),a0
	move.l	ThisTask(a0),OurTask
	
	;------ Allocate a StdIORequest block for console I/O.
	bsr	AllocIOBlock
	move.l	d0,ConIOBlock

	;------ Allocate a StdIORequest block for input.device I/O.
	bsr	AllocIOBlock
	move.l	d0,InpIOBlock

	;------ Get a signal for us to use...
	bsr	AllocSignal

	;------ Open the input.device
	bsr	OpenInputDevice

	;------ Set up the interrupt handler node, and tell the input.dev
	;------ about it.
	bsr	SetInputInterrupt
	rts


*------------------------------------------------------------------------------
AllocIOBlock
	;------ Allocate a STDIO block.

	;------ First, create the message port
	moveq	#-1,d0
	ExecF	AllocSignal

	move.l	d0,d2			; Save this for the moment...
	cmp.l	#-1,d0			; Did we get it?
	beq.s	0$

	;------ Allocate memory for the port:
	move.l	#(MEMF_PUBLIC!MEMF_CLEAR),d1
	move.l	#MP_SIZE,d0
	LibF	AllocMem

	tst.l	d0				; Any memory free?
	beq.s	2$				; Nope, exit w/error.

	move.l	d0,a2				; Make me a pointer to this...
	
	;------ Set up the port values
	move.b	#NT_MSGPORT,LN_TYPE(a2)		; type = message port
	move.b	#PA_SIGNAL,MP_FLAGS(a2)		; signal me...
	move.b	d2,MP_SIGBIT(a2)		; ...with this signal
	move.l	OurTask(pc),MP_SIGTASK(a2)	; signal our task

	;------ Add the port to the list
	lea	MP_MSGLIST(a2),a0
	NEWLIST	a0

	;------ Now create the request block
	move.l	#IOSTD_SIZE,d0
	move.l	#(MEMF_CLEAR!MEMF_PUBLIC),d1
	ExecF	AllocMem
	
	tst.l	d0				; Any mem?
	beq.s	0$				; Nope, free the port & exit.
	
	move.l	d0,a0
	move.b	#NT_MESSAGE,LN_TYPE(a0)
	move.w	#IOSTD_SIZE,MN_LENGTH(a0)
	move.l	a2,MN_REPLYPORT(a0)
	rts

0$	;------ Free the port
	jsr	FreePort
	bra.s	4$

2$	;------ Free the signal
	move.l	d2,d0	
	LibF	FreeSignal
4$	clr.l	d0
	rts


*------------------------------------------------------------------------------
AllocSignal
	;------ Get a signal for us to use, and make it into a signal mask
	;------ for the Exec Wait() function.

	moveq	#-1,d0
	ExecF	AllocSignal
	tst.l	d0
	beq.s	0$

	moveq	#1,d1
	lsl.l	d1,d0
	move.l	d0,OurSigMask
0$	rts

	
*------------------------------------------------------------------------------
OpenInputDevice
	;------ Umm... open the input.device
	
	clr.l	d1			; Flags
	clr.l	d0			; Unit number
	lea	IDName(pc),a0		; Input device name
	move.l	InpIOBlock,a1		; The IORequest block
	ExecF	OpenDevice
	rts


*------------------------------------------------------------------------------
SetInputInterrupt
	;------ Set up the interrupt handler node, and tell the input.dev
	;------ to install it.
	
	;------ Note that we set the priority to 51. This magic number 
	;------ assures us that we will get the event before intuition
	;------ because intuition only has a priority of 50.

	lea	IntRec,a0
	
	clr.l	IS_DATA(a0)			; No data...
	move.l	#IHandler,IS_CODE(a0)		; Point to our handler
	move.b	#51,LN_PRI(a0)			; Set the priority

	;------ Set up the IO request block
	move.l	InpIOBlock,a1

	move.w	#IND_ADDHANDLER,IO_COMMAND(a1)
	move.l	a0,IO_DATA(a1)

	;------ Send the bitch!
	ExecF	DoIO
	rts


*------------------------------------------------------------------------------
OpenCon	
	;------ Open the console device for our window...
	move.l	ConIOBlock,a1
	move.l	TheWindow,IO_DATA(a1)
	move.l	#wd_Size,IO_LENGTH(a1)
	lea	ConName(pc),a0
	clr.l	d0
	clr.l	d1
	ExecF	OpenDevice
	rts


*------------------------------------------------------------------------------
OpenTheWindow

	;------ Open the window on the screen pointed to by a1.

	move.l	NWPointer,a0		; Snag ptr to NewWindow struct
	move.w	sc_Flags(a1),d0
	and.w	#CUSTOMSCREEN,d0
	
	move.l	a1,nw_Screen(a0)
	move.w	d0,nw_Type(a0)

	IntF	OpenWindow
	move.l	d0,TheWindow
	rts


*------------------------------------------------------------------------------
CloseAll

	;------ Close the console device
	move.l	ConIOBlock,a1
	ExecF	CloseDevice

	;------ Close the window
	move.l	TheWindow,a0
	IntF	CloseWindow
	rts


*------------------------------------------------------------------------------
*
*  This section contains all of the console I/O routines.
*  
*------------------------------------------------------------------------------

*------------------------------------------------------------------------------
ConReadChar
	;------ Read one character from the console.
	move.l	ConIOBlock(pc),a1
	move.w	#CMD_READ,IO_COMMAND(a1)
	move.l	#1,IO_LENGTH(a1)

	move.l	#CharBuf,IO_DATA(a1)
	ExecF	DoIO
	rts

*------------------------------------------------------------------------------
ConReadLine
	;------ Read a line from the screen and stick it in (a2)
	;------ and null-terminate it in the process.

	move.l	a2,d2				; Save this address...
	moveq.l	#1,d3				; Save character count
	
0$	;------ Get a character
	bsr	ConReadChar

	;------ Check for a BACKSPACE character.
	cmp.b	#BACKSPACE,CharBuf
	bne.s	2$

	bsr	EditBACKSPACE
	bra.s	0$

2$	;------ If we have a CR char, print a newline and return.
	cmp.b	#RETURN,CharBuf
	bne.s	4$

	lea	CrLf(pc),a0
	bsr	ConWriteLine
	clr.b	(a2)
	rts

4$	;------ Increment the buffer pointer and fetch another character.
	cmp.w	#77,d3
	bcc.s	0$

	;------ Echo the character. 
	bsr	ConWriteChar

	move.b	CharBuf,(a2)+
	addq.l	#1,d3
	bra.s	0$


*------------------------------------------------------------------------------
EditBACKSPACE
	;------ Edit the BACKSPACE character.

	;------ If it is the first character on the line, do nothing.
	cmp.l	a2,d2
	beq.s	0$

	;------ Print the backspace, and back up 1 space. 
	lea	BackSpaceStr(pc),a0
	bsr	ConWriteLine
	subq.l	#1,a2
	subq.l	#1,d3
0$	rts	


*------------------------------------------------------------------------------
ConWriteChar
	;------ Write the character in CharBuf on the screen.
	lea	CharBuf(pc),a0
	moveq.l	#1,d0
	bra.s	ConWrite

*------------------------------------------------------------------------------
ConWriteLine
	;------ Put the null terminated string (pointed to by a0) on the
	;------ screen.
	moveq.l	#-1,d0			; Fall thru to ConWrite...

*------------------------------------------------------------------------------
ConWrite
	;------ Print d0 chars of string a0. If d0 is -1, we expect a0 to be
	;------ a null-terminated string.
	move.l	ConIOBlock,a1
	
	move.l	a0,IO_DATA(a1)
	move.l	d0,IO_LENGTH(a1)
	move.w	#CMD_WRITE,IO_COMMAND(a1)
	ExecF	DoIO
	rts


*------------------------------------------------------------------------------
*
*  This section handles the insertion of input events into the event stream.
*  
*------------------------------------------------------------------------------

*------------------------------------------------------------------------------
ConvertCharacter
	;------ Convert the character pointed to by a2 to an Amiga-useable
	;------ qualifier/code pair.
	clr.w	d0
	clr.w	d1
	lea	KeyTable(pc),a0

0$	move.b	(a2)+,d0
	beq	99$

	;------ Test for an escape character.
	cmp.b	#'\',d0
	bne	50$

	;------ Process escape character.
	move.b	(a2)+,d0

	;------ Test for left shift
	cmp.b	#'s',d0
	bne.s	2$

	bset	#L_SHIFT,d1
	bra.s	0$

2$	;------ Test for right shift
	cmp.b	#'S',d0
	bne.s	4$

	bset	#R_SHIFT,d1
	bra	0$

4$	;------ Test for left alt
	cmp.b	#'a',d0
	bne.s	6$

	bset	#L_ALT,d1
	bra	0$

6$	;------ Test for right alt
	cmp.b	#'A',d0
	bne.s	8$

	bset	#R_ALT,d1
	bra	0$

8$	;------ Not a case-sensitive key at this point, make it upper
	;------ case...
	Ucase	d0

	;------ Add Control qualifier.
	cmp.b	#'C',d0
	bne.s	10$

	bset	#CONTROL,d1
	bra	0$			

10$	;------ Process Left-Amiga qualifier.
	cmp.b	#'L',d0
	bne.s	16$

	bset	#L_AMIGA,d1
	bra	0$			
	
	;------ Process Right-Amiga qualifier.
16$	cmp.b	#'R',d0
	bne.s	18$

	bset	#R_AMIGA,d1
	bra	0$			
	
18$	;------ Process Keypad qualifier.
	cmp.b	#'K',d0
	bne.s	24$

	;------ The keypad keys present a special case: If the key is between
	;------ 0 and 9, set the keypad bit, else don't.
	move.b	(a2)+,d0
	cmp.b	#'0',d0
	blt.s	20$

	bset	#KPAD,d1

20$	lea	KeypadTable(pc),a0
	sub.b	#'(',d0
	lsl.b	#1,d0
	move.b	1(a0,d0.w),d0		; Fetch character code.
	rts		


24$	;------ Process Function keys.
	cmp.b	#'F',d0
	bne.s	28$

	move.b	(a2)+,d0
	cmp.b	#'1',d0
	bne.s	26$

	cmp.b 	#'0',(a2)
	bne.s	26$
	
	;------ Process f10
	addq.l	#1,a2
	move.b	#('9'+1),d0
	
26$	add.b	#31,d0
	rts

28$	;------ Process Help key
	cmp.b	#'H',d0
	bne.s	30$

	move.b	#$5f,d0
	rts

30$	;------ Process Cursor keys.
	cmp.b	#'^',d0
	bne.s	50$

	move.b	(a2)+,d0
	Ucase	d0

	cmp.b	#'U',d0
	bne.s	32$

	move.b	#$4c,d0
	rts

32$	cmp.b	#'D',d0
	bne.s	34$

	move.b	#$4d,d0
	rts

34$	cmp.b	#'L',d0
	bne.s	36$

	move.b	#$4f,d0
	rts

36$	cmp.b	#'R',d0
	bne.s	38$

	move.b	#$4e,d0
	rts

38$	bra	0$

	
50$	;------ Not an escape code, just print the character.
	sub.w	#27,d0			; Subtract table base.
	lsl.b	#1,d0			; Calc table offset.	

	move.b	0(a0,d0.w),d2
	or.b	d2,d1			; Form character qualifer.

	move.b	1(a0,d0.w),d0		; Fetch character code.
99$	rts


	
*------------------------------------------------------------------------------
InsertString
	;------ Insert the string into the input event stream
	bsr	ConvertCharacter
	beq.s	0$

	bsr	InsertEvent
	bra.s	InsertString
0$	rts
	

*------------------------------------------------------------------------------
InsertEvent
	;------ Insert the event into the input stream. The event will be
	;------ coded as: code=d0, qualifier=d1
	lea	MyEventDn(pc),a0
	lea	MyEventUp(pc),a1

	;------ Set up the input event.
	move.l	a1,ie_NextEvent(a0)
	move.b	#IECLASS_RAWKEY,ie_Class(a0)
	move.w	d0,ie_Code(a0)
	move.w	d1,ie_Qualifier(a0)
	clr.w	ie_X(a0)
	clr.w	ie_Y(a0)
	clr.l	ie_TimeStamp(a0)
	clr.l	(ie_TimeStamp+4)(a0)

	;------ Set up the input event.
	clr.l	ie_NextEvent(a1)
	move.b	#IECLASS_RAWKEY,ie_Class(a1)
	add.w	#128,d0
	move.w	d0,ie_Code(a1)
	move.w	d1,ie_Qualifier(a1)
	clr.w	ie_X(a1)
	clr.w	ie_Y(a1)
	clr.l	ie_TimeStamp(a1)
	clr.l	(ie_TimeStamp+4)(a1)

	;------ Set up the IORequest block
	move.l	InpIOBlock(pc),a1
	move.w	#IND_WRITEEVENT,IO_COMMAND(a1)
	clr.b	IO_FLAGS(a1)
	move.l	#ie_SIZEOF,IO_LENGTH(a1)
	move.l	a0,IO_DATA(a1)

	;------ Send the event
	ExecF	DoIO
	rts

*------------------------------------------------------------------------------
*
*  These two routines are input handler related
*  
*------------------------------------------------------------------------------

*------------------------------------------------------------------------------
LookupKey
	;------ See if input event matches a defined key. a2 points to 
	;------ input event struct.

	;------ First, form search key.
	move.w	ie_Qualifier(a2),d0
	and.w	#%111111111,d0		; don't care about upper bits
	
	;------ Add the keycode to the qualifier, and look it up.
	swap	d0
	move.w	ie_Code(a2),d0
	bsr	LookForKey
	beq.s	4$	

	;------ If we get here, we found the key. EnQ the event, set 
	;------ Event to DOMAC, and leave.
	move.l	a0,a3			; Get macro rec address for queueing
	bsr	EnQ
	move.b	#DOMAC,Event
4$	rts

	
	
*------------------------------------------------------------------------------
IHandler
	;------ When called, we get a pointer to the input-event chain
	;------ in a0, and a pointer to our data in a1. We don't need
	;------ a1 tho. 

	movem.l	a2/a3/a6,-(sp)		; Save these
	move.l	a0,a2

	clr.b	Event				
	
0$	cmp.b	#IECLASS_RAWKEY,ie_Class(a2)
	bne.s	4$

	bsr	LookupKey
	bne.s	2$

	cmp.w	#HELPKEY,ie_Code(a2)
	bne.s	4$
	btst.b	#IEQUALIFIERB_LCOMMAND,(ie_Qualifier+1)(a2)
	beq.s	4$

	move.b	#DOCMD,Event		; Indicate: open command window

2$	;------ This is our event, remove it from the list and signal
	;------ our task.
	move.l	ie_NextEvent(a2),a2

	;------ Signal the waiting task.
	move.l	OurSigMask(pc),d0
	move.l	OurTask(pc),a1
	ExecF	Signal

4$	move.l	a2,d0			; Pass return value in d0
	movem.l	(sp)+,a2/a3/a6
	rts
	


*------------------------------------------------------------------------------
*
*  This is the Data section!
*  
*------------------------------------------------------------------------------
ConIOBlock	ds.l	1		; Pointer to console.dev IOblock.
InpIOBlock	ds.l	1		; Pointer to input.dev IOblock.
IntRec		ds.b	IS_SIZE		; The interrupt handler node
MyEventDn	ds.b	ie_SIZEOF	; My input event structure (key down).
MyEventUp	ds.b	ie_SIZEOF	; My input event structure (key up).
ErrorSp		ds.l	1		; Stack ptr used for error recovery.
CmdQ		ds.w	128		; Queue for macros.
QF		ds.w	1		; Front of Q pointer.
QB		ds.w	1		; Back of Q pointer.
QC		ds.w	1		; Elements in Q.

DefinedKeys	;------ The first record in the chain
		dc.l	0	; next
		dc.l	0	; prev
		dc.w	0	; key
		dc.b	0	; definition
		ds.b	79

OurTask		ds.l	1		; Pointer to our TCB.
OurSigMask	ds.l	1		; Signal mask for our port.
TheWindow	ds.l	1		; Pointer to our window structure.
StringBuf	ds.b	160		; Buffer for reading strings

;------ The following table is used to convert ascii characters to their 
;------ keyboard equivalents. We put the qualifier in the first byte, and 
;------ the key-code in the second. The table starts with an <escape> character.
KeyTable	
		;------ Punctuation and digits (<esc> to '@')
		dc.w	$0045,$0000,$0000,$0000,$0000,$0040,$0101,$012a,$0103
		dc.w	$0104,$0105,$0107,$002a,$0109,$010a,$0108,$010c,$0038
		dc.w	$000b,$0039,$003a,$000a,$0001,$0002,$0003,$0004,$0005
		dc.w	$0006,$0007,$0008,$0009,$0129,$0029,$0138,$000c,$0139
		dc.w	$013a,$0102
		
		;------ Upper case letters
		dc.w	$0120,$0135,$0133,$0122,$0112,$0123,$0124,$0125,$0117
		dc.w	$0126,$0127,$0128,$0137,$0136,$0118,$0119,$0110,$0113
		dc.w	$0121,$0114,$0116,$0134,$0111,$0132,$0115,$0131
		
		;------ Punctuation
		dc.w	$001a,$000d,$001b,$0106,$010b,$0000

		;------ Lower case letters
		dc.w	$0020,$0035,$0033,$0022,$0012,$0023,$0024,$0025,$0017
		dc.w	$0026,$0027,$0028,$0037,$0036,$0018,$0019,$0010,$0013
		dc.w	$0021,$0014,$0016,$0034,$0011,$0032,$0015,$0031

		;------ Punctuation
		dc.w	$011a,$010d,$011b,$0100,$0046

KeypadTable
		;------ Keypad keys starting with '('
		dc.w	$005a,$005b,$005d,$005e,$0000,$004a,$003c,$005c
		dc.w	$000f,$001d,$001e,$001f,$002d,$002e,$002f,$003d,$003e
		dc.w	$003f


CharBuf		ds.b	1			; Buffer for reading characters.
KillFlag	ds.b	1			; 'kill program' flag.
Event		ds.b	1			; 'Type of input event' flag.
		cnop	0,2

CrLf		dc.b	13,10,0
BackSpaceStr	dc.b	$9b,$44,$9b,$50,0	; Back up and delete char.
ClrWindowStr	dc.b	12,0
PromptStr	dc.b	'>',0

TheTitle	dc.b	'Keys V1.0  --  ',0
ChoicePrompt	dc.b	'Enter: (K)ill, (D)efine, (R)emove, (L)oad, (S)ave, (V)iew',0
DefinePrompt	dc.b	'Describe key to Define:',0
RemovePrompt	dc.b	'Describe key to Remove:',0
MacPrompt	dc.b	'Enter key definition:',0
LoadPrompt	dc.b	'Enter file name to Load:',0
SavePrompt	dc.b	'Enter file name to Save:',0
ViewPrompt	dc.b	'Key listing. Press any key to exit...',0

LoadingMsg	dc.b	'Loading...',0
SavingMsg	dc.b	'Saving...',0

ErNoMem		dc.b	'Error: No memory available',0
ErNoFile	dc.b	'Error: File not found',0
ErNoKey		dc.b	'Error: Key not defined',0
ErDefTooLong	dc.b	'Error: Macro definition exceeds 76 characters',0
ConName		dc.b	'console.device',0
IDName		dc.b	'input.device',0
CLIName		dc.b	'Initial CLI',0
		end	
