*****************************************************************************
*  Program: Koala.asm
*  Descrip: Test driver for the Koala Pad.  This program is not a device
*           driver.  It is only a little test bed for the using the Koala Pad
*           on an Amiga.  It has only been tested with a C64 Version - Koala
*           Model 002 Pad.  It has been tested on an A1000 running V1.3 and
*           an A3000 running V2.0, and works fine with both the CLI and with
*           DPaint III.
*
*           There is one small problem using this under V1.3, however.  Under
*           V1.3 Intuition looks directly to the hardware for mousebutton
*           events that relate to Intuition gadgets and menus.  Therefore, if
*           using the Koala Pad under V1.3, gadget and menu selection will
*           hang while Intuition is waiting for a corresponding hardware
*           event which never happens.  This deadlock can be broken by simply
*           pressing the appropriate button on the real mouse, in Port 0.
*           There is no such problem operating under V2.0.
*           
*    Usage: Plug the Koala Pad into port 1 (after making necessary changes
*           shown in accompanying IFF file).  Run this software:
*
*             1> run Test
*
*           To kill this program, enter the following line:
*
*             1> Break # C
*
*           where # is the Process number.
*
*   Author: Jeff Lavin
*  History: 02/01/90 JL V0.10 Created
******************************************************************************
*   DISCLAIMER  DISCLAIMER  DISCLAIMER  DISCLAIMER  DISCLAIMER  DISCLAIMER
*
* This program is unconditionally placed in the Public Domain by Jeff Lavin,
* on January 19, 1991.
*
* This program is supplied "as is" without Warrantee of any kind, either
* expressed or implied.  The entire risk as to the results of performance is
* assumed by you.  In no event shall the author be liable for direct or
* consequential damages resulting from the use of this program.
*
* By copying, distributing and/or using this program you indicate your
* acceptance of these terms.
*****************************************************************************

;Set Tabs           |       |                 |       |

	ifnd	__m68
	fail	'Wrong assembler, buy Macro68!'
	endc
	exeobj
	objfile	'ram:Test'

MyBSS	clrso
InitialSP	so.l	1
DosBase	so.l	1
IntBase	so.l	1
MyTask	so.l	1
MySignal	so.l	1
PotgoBase	so.l	1
PotgoBits	so.l	1
PotX_0	so.b	1
PotX_1	so.b	1
PotY_0	so.b	1
PotY_1	so.b	1
B3_Stat	so.b	1
B3_Old	so.b	1
B4_Stat	so.b	1
B4_Old	so.b	1
MyFlags	so.b	1
Pad	so.b	1
InputReq	so.b	IOSTD_SIZE
VBInterrupt	so.b	IS_SIZE
PhonyEvent	so.b	ie_SIZEOF
BSS_SIZEOF	soval

POSTB_B3_DN	equ	0
POSTB_B3_UP	equ	1
POSTB_B4_DN	equ	2
POSTB_B4_UP	equ	3
POSTB_POSITION	equ	4
POSTB_QUIT	equ	5

_custom	equ	$DFF000	;Custom chip base

*** Begin Mainline

Test	move.l	#MEMF_PUBLIC!MEMF_CLEAR,d1
	move.w	#BSS_SIZEOF,d0
	movea.l	(SysBase).w,a6
	SYS	AllocMem	;Get our BSS area
	movea.l	d0,a5
	tst.l	d0
	bne.b	1$
	moveq	#ERROR_NO_FREE_STORE,d2
	moveq	#RETURN_FAIL,d0
	rts

1$	move.l	sp,(InitialSP,a5)

	lea	(DosName,pc),a1
	moveq	#0,d0
	SYS	OpenLibrary	;Open dos.library
	move.l	d0,(DosBase,a5)
	beq	Cleanup

	lea	(IntName,pc),a1
	moveq	#0,d0
	SYS	OpenLibrary	;Open intuition.library
	move.l	d0,(IntBase,a5)
	beq	Cleanup

	movea.l	(ThisTask,a6),a1
	move.l	a1,(MyTask,a5)
	move.b	(pr_MsgPort+MP_SIGBIT,a1),d1
	moveq	#0,d0
	bset	d1,d0
	move.l	d0,(MySignal,a5)	;Get out process signal

	lea	(IPortName,pc),a0
	moveq	#0,d0
	bsr	CreatePort	;Get a MsgPort
	tst.l	d0
	beq	Cleanup

	lea	(InputReq,a5),a1	;Init the IOReq
	move.b	#NT_MESSAGE,(LN_TYPE,a1)
	move.b	#0,(LN_PRI,a1)
	move.l	d0,(MN_REPLYPORT,a1)
	move.w	#IOSTD_SIZE,(MN_LENGTH,a1)
	moveq	#0,d0
	moveq	#0,d1
	lea	(InpName,pc),a0
	SYS	OpenDevice	;Open the input.device
	tst.l	d0
	bne.b	Cleanup

	lea	(PotgoName,pc),a1
	moveq	#33,d0
	movea.l	(SysBase).w,a6
	SYS	OpenResource
	move.l	d0,(PotgoBase,a5)
	beq.b	Cleanup

	movea.l	d0,a6
	move.l	#$F001,d0	;Right port & start bits
	move.l	d0,d2
	SYS	AllocPotBits
	move.l	d0,(PotgoBits,a5)

	cmp.l	d2,d0	;Get 'em all?
	bne.b	Cleanup	;Branch if no

	moveq	#0,d0	;Input bits
	move.l	#$A000,d1	;Input mask
	SYS	WritePotgo

	lea	(VBInterrupt,a5),a1 ;Init & Add interrupt server
	move.b	#NT_INTERRUPT,(LN_TYPE,a1)
	move.b	#0,(LN_PRI,a1)
	lea	(ServerName,pc),a0
	move.l	a0,(LN_NAME,a1)
	move.l	a5,(IS_DATA,a1)
	lea	(JoyServer,pc),a0
	move.l	a0,(IS_CODE,a1)
	moveq	#INTB_VERTB,d0
	movea.l	(SysBase).w,a6
	SYS	AddIntServer

	bsr.b	Main_Loop

	lea	(VBInterrupt,a5),a1 ;Remove interrupt server
	moveq	#INTB_VERTB,d0
	SYS	RemIntServer

Cleanup	move.l	(PotgoBits,a5),d0	;Free pot bits
	beq.b	1$
	move.l	(PotgoBase,a5),d1
	beq.b	1$
	movea.l	d1,a6
	SYS	FreePotBits

1$	movea.l	(SysBase).w,a6
	lea	(InputReq,a5),a2
	tst.l	(IO_DEVICE,a2)
	beq.b	2$
	movea.l	a2,a1
	SYS	CloseDevice	;Close the input.device

2$	move.l	(MN_REPLYPORT,a2),d0
	beq.b	3$
	movea.l	d0,a1
	bsr	DeletePort

3$	move.l	(IntBase,a5),d0
	beq.b	4$
	movea.l	d0,a1
	SYS	CloseLibrary

4$	movea.l	(a5),sp	;InitialSP
	movea.l	a5,a1
	move.w	#BSS_SIZEOF,d0
	SYS	FreeMem
	moveq	#0,d0
	rts

Main_Loop	move.l	(MySignal,a5),d0
	SYS	Wait

;Find out what type of event to send.  If BUTTON, just send button up or down.
;If RELATIVEMOUSE, find out where mouse is in current screen, and send relative
;to that, this converts absolute pointer positions into relative movements.

WriteEvent	bsr	CheckAbort	;^C?
	bne.b	1$	;Yes, abort
	tst.b	(MyFlags,a5)
	beq.b	Main_Loop
	bclr	#POSTB_QUIT,(MyFlags,a5)
	beq.b	2$
1$	rts

2$	lea	(InputReq,a5),a1
	move.w	#IND_WRITEEVENT,(IO_COMMAND,a1)
	clr.b	(IO_FLAGS,a1)
	moveq	#ie_SIZEOF,d0
	move.l	d0,(IO_LENGTH,a1)
	lea	(PhonyEvent,a5),a0	;Our InputEvent
	clr.l	(a0)		;No ie_NextEvent
	move.l	a0,(IO_DATA,a1)
	move.b	#IECLASS_RAWMOUSE,(ie_Class,a0)
	clr.b	(ie_SubClass,a0)

	moveq	#IECODE_RBUTTON,d0
	move.w	#IEQUALIFIER_RBUTTON,d1
	bclr	#POSTB_B3_DN,(MyFlags,a5)
	bne.b	3$

	move.w	#IECODE_UP_PREFIX!IECODE_RBUTTON,d0
	move.w	#IEQUALIFIER_RBUTTON,d1
	bclr	#POSTB_B3_UP,(MyFlags,a5)
	bne.b	3$

	moveq	#IECODE_LBUTTON,d0
	move.w	#IEQUALIFIER_LEFTBUTTON,d1
	bclr	#POSTB_B4_DN,(MyFlags,a5)
	bne.b	3$

	move.w	#IECODE_UP_PREFIX!IECODE_LBUTTON,d0
	move.w	#IEQUALIFIER_LEFTBUTTON,d1
	bclr	#POSTB_B4_UP,(MyFlags,a5)
	beq.b	4$

3$	move.w	d0,(ie_Code,a0)
	move.w	d1,(ie_Qualifier,a0)
	clr.w	(ie_X,a0)
	clr.w	(ie_Y,a0)
	bra.b	SendEvent

4$	move.w	#IECODE_NOBUTTON,(ie_Code,a0)
	bclr	#POSTB_POSITION,(MyFlags,a5)
	beq	WriteEvent	;Shouldn't ever take this branch
	move.w	#IEQUALIFIER_RELATIVEMOUSE,(ie_Qualifier,a0)

	movem.l	a0-a1/a6,-(sp)
	movea.l	(IntBase,a5),a6
	moveq	#0,d0
	SYS	LockIBase
	move.l	d0,-(sp)	;Lock

	movea.l	(ib_ActiveScreen,a6),a1
	moveq	#0,d2
	move.w	(sc_Width,a1),d2
	divu.w	#24,d2	;Compute 8X scale factor
	addq.w	#1,d2	;and round up.

	moveq	#0,d0
	move.b	(PotX_0,a5),d0
	subq.w	#2,d0	;Normalize
	mulu.w	d0,d2	;PadX * Scale(Width)
	lsr.w	#3,d2	;Normalize
	sub.w	(sc_MouseX,a1),d2	;Scale(PadX) - MouseX

	moveq	#0,d3
	move.w	(sc_Height,a1),d3
	divu.w	#24,d3	;Compute 8X scale factor
	addq.w	#1,d3	;and round up.

	moveq	#0,d1
	move.b	(PotY_0,a5),d1
	subq.w	#2,d1	;Normalize
	mulu.w	d1,d3	;PadY * Scale
	lsr.w	#3,d3	;Normalize
	sub.w	(sc_MouseY,a1),d3	;Scale(PadY) - MouseY

	movea.l	(sp)+,a0
	SYS	UnlockIBase
	movem.l	(sp)+,a0-a1/a6

	move.w	d2,(ie_X,a0)
	move.w	d3,(ie_Y,a0)

SendEvent	clr.l	(ie_TimeStamp+TV_SECS,a0)
	clr.l	(ie_TimeStamp+TV_MICRO,a0)
	SYS	DoIO
	bra	WriteEvent

* Vertical Blanking Routine for proportional joystick.  This routine reads
* the right gameport hardware during every vertical blanking interrupt and
* saves the data for later use.  A1 points to interrupt data area.  Must
* save A0 because of a bug in the graphics.library server chain.

JoyServer	movem.l	d2-d3/a0,-(sp)
	lea	(_custom),a0

	move.b	(PotX_0,a1),(PotX_1,a1)
	move.b	(PotY_0,a1),(PotY_1,a1)

	move.w	(pot1dat,a0),d0	;Get new pot data
	move.b	d0,(PotX_0,a1)	;Current X
	lsr.w	#8,d0
	move.b	d0,(PotY_0,a1)	;Current Y

	moveq	#0,d0
	moveq	#0,d2
	move.b	(PotX_0,a1),d2
	move.b	(PotX_1,a1),d0
	add.w	d0,d2
	lsr.w	#1,d2	;Divide by 2
	move.b	(PotX_0,a1),d0	;Retrieve current value
	move.b	d2,(PotX_0,a1)	;And store average value
	sub.b	d0,d2
	bmi.b	1$
	neg.b	d2	;Absolute X difference

1$	moveq	#0,d1
	moveq	#0,d3
	move.b	(PotY_0,a1),d3
	move.b	(PotY_1,a1),d1
	add.w	d1,d3
	lsr.w	#1,d3	;Divide by 2
	move.b	(PotY_0,a1),d1	;Retrieve current value
	move.b	d3,(PotY_0,a1)	;And store average value
	sub.b	d1,d3
	bmi.b	2$
	neg.b	d3	;Absolute Y difference

2$	cmpi.b	#2,d0	;Absolute X value
	blo.b	3$
	cmpi.b	#2,d2
	bhs.b	4$

3$	cmpi.b	#2,d1	;Absolute Y value
	blo.b	Buttons
	cmpi.b	#2,d3
	blo.b	Buttons
4$	bset	#POSTB_POSITION,(MyFlags,a1)

Buttons	move.b	(B3_Stat,a1),d2	;Save old values
	move.b	d2,(B3_Old,a1)
	move.b	(B4_Stat,a1),d3
	move.b	d3,(B4_Old,a1)

	move.w	(joy1dat,a0),d0	;Get joystick button positions
	btst	#1,d0
	sne	(B3_Stat,a1)
	btst	#9,d0
	sne	(B4_Stat,a1)

	move.b	(B3_Stat,a1),d0
	cmp.b	d0,d2	;Has Button3 changed?
	beq.b	2$	;Branch if no
	tst.b	d0	;Up or down?
	beq.b	1$	;Branch if up
	bset	#POSTB_B3_DN,(MyFlags,a1)
	bra.b	Signal
1$	bset	#POSTB_B3_UP,(MyFlags,a1)
	bra.b	Signal

2$	move.b	(B4_Stat,a1),d1
	cmp.b	d1,d3	;Has Button4 changed?
	beq.b	4$	;Branch if no
	tst.b	d1	;Up or down?
	beq.b	3$	;Branch if up

	bset	#POSTB_B4_DN,(MyFlags,a1)
	bra.b	Signal
3$	bset	#POSTB_B4_UP,(MyFlags,a1)
	bra.b	Signal

4$	tst.b	(MyFlags,a1)
	beq.b	Restart

Signal	move.l	a1,-(sp)
	move.l	(MySignal,a1),d0
	movea.l	(MyTask,a1),a1
	movea.l	(SysBase).w,a6
	SYS	Signal
	movea.l	(sp)+,a1

Restart	moveq	#1,d0	;Restart conversion
	moveq	#1,d1
	movea.l	(PotgoBase,a1),a6
	SYS	WritePotgo
	movem.l	(sp)+,d2-d3/a0
	moveq	#0,d0
	rts

*****************************************************************************
* NAME:     CreatePort()
* FUNCTION: Create a named MsgPort.
* INPUT:    A0 = Name of port.
*           D0 = Priority of port.
* RESULT:   D0 = MsgPort, or NULL.
* SCRATCH:  D1-D2/A0-A2
*****************************************************************************

CreatePort	movem.l	d0/a0,-(sp)
	moveq	#-1,d0
	SYS	AllocSignal
	cmpi.l	#-1,d0
	beq.b	1$
	move.l	d0,d2

	move.l	#MEMF_PUBLIC!MEMF_CLEAR,d1
	moveq	#MP_SIZE,d0
	SYS	AllocMem
	movea.l	d0,a2
	tst.l	d0
	bne.b	2$
	move.l	d2,d0
	SYS	FreeSignal
1$	addq.l	#8,sp
	moveq	#0,d0
	bra.b	3$
 
2$	movem.l	(sp)+,d0/a0
	move.l	a0,(LN_NAME,a2)
	move.b	d0,(LN_PRI,a2)
	move.b	#NT_MSGPORT,(LN_TYPE,a2)
	move.b	d2,(MP_SIGBIT,a2)
	move.l	(ThisTask,a6),(MP_SIGTASK,a2)
	movea.l	a2,a1
	SYS	AddPort
	move.l	a2,d0
3$	rts

*****************************************************************************
* NAME:     DeletePort()
* FUNCTION: Delete a named MsgPort.
* INPUT:    A1 = Ptr to a MsgPort.
* RESULT:   None
* SCRATCH:  D0-D1/A0-A1
*****************************************************************************

DeletePort	move.l	a1,-(sp)
	SYS	RemPort
	movea.l	(sp),a1
	move.b	#-1,(LN_TYPE,a1)
	move.l	#-1,(MP_MSGLIST,a1)
	moveq	#0,d0
	move.b	(MP_SIGBIT,a1),d0
	SYS	FreeSignal
	moveq	#MP_SIZE,d0
	movea.l	(sp)+,a1
	SYS	FreeMem
	rts
 
*****************************************************************************
* NAME:     CheckAbort()
* FUNCTION: Check for a ^C, and echo if found.
* INPUTS:   None.
* RETURN:   D0 = 1 if ^C, else 0.
* SCRATCH:  D0-D1/A0-A1
*****************************************************************************

CheckAbort	move.l	a6,-(sp)
	moveq	#0,d0
	move.l	#SIGBREAKF_CTRL_C,d1
	movea.l	(SysBase).w,a6
	SYS	SetSignal
	andi.l	#SIGBREAKF_CTRL_C,d0
	beq.s	2$

	movea.l	(DosBase,a5),a6
	SYS	Output
	move.l	d0,d1	;FileHandle
	beq.b	1$	;No output, but abort anyway
	lea	(BrkMsg,pc),a0
	move.l	a0,d2	;Buffer
	moveq	#BrkLen,d3	;Length
	SYS	Write
1$	moveq	#1,d0
2$	movea.l	(sp)+,a6
	rts

DosName	cstr	'dos.library'
IntName	cstr	'intuition.library'
PotgoName	cstr	'potgo.resource'
InpName	cstr	'input.device'
ServerName	cstr	'joy.server'
IPortName	cstr	'joy.port'
BrkMsg	cstr	10,'**** Break',10
BrkLen	equ	*-BrkMsg

	end
