home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Developer CD v1.2
/
amidev_cd_12.iso
/
reference
/
amiga_mail_vol1
/
input
/
inputresource
< prev
next >
Wrap
Text File
|
1990-01-26
|
21KB
|
716 lines
(c) Copyright 1989 Commodore-Amiga, Inc. All rights reserved.
The information contained herein is subject to change without notice, and
is provided "as is" without warranty of any kind, either expressed or implied.
The entire risk as to the use of this information is assumed by the user.
Allocation of Input and
Other System Resources
by Bob Burns
In most system designs there is a trade-off between ease of use and
performance. The Amiga is no exception. Historically, high performance
applications have resorted to taking over the machine in order to get direct
access to system resources. This is something we would like to avoid because
once the machine is taken over, there is no exit. The user must reboot.
The purpose of this article is to present some techniques that let games
and other special applications take over those portions of the system that
they need, yet gracefully return them when no longer needed.
Input Resources
---------------
The Input Task
A useful metaphor for describing the input task on the Amiga is the
"food-chain". Raw input events are passed through a linked list of handlers.
Each handler processes the input events it receives and returns a new set to
be "consumed" by the next input handler in the chain.
The primal input events are designated RAWKEY, RAWMOUSE, TIMER and DISK.
The RAWKEY, RAWMOUSE and TIMER input events are started up the food chain by
the input.device itself in response to its continuing IO requests to the
keyboard, gameport, and timer devices. The DISK events are inserted into
the chain by the DOS.
The two standard consumers in the food chain are Intuition and the console
device. Intuition in particular is prolific in the variety of new input
events it may produce in response to a primal event. Another consumer often
found in the food chain is a generic hotkey program. These insert themselves
in the food chain before either Intuition or the console.
The food chain is implemented as a a prioritized list, with Intuition at a
priority of 50 and the console device at 20. For more on input events, see
the devices/inputevents.h header.
Input Device Commands
Let's quickly review the input device commands you can use to control the
food chain. Insertion and removal of a new consumer in the input device food
chain is performed by the input.device commands AddHandler and RemHandler.
The command WriteEvent is used to propagate an event through the input food
chain without being on it.
The commands SetThresh and SetPeriod are used to modifiy the key repeat rate.
Finally, the commands SetMPort, SetMType, and SetMTrig are used to modify the
gameport controller that is to be used for mouse input. The use of these
commands is illustrated in the example code shown below.
The joymouse.c example uses the commands to move the mouse to the second
gameport device so that the first gameport can be used with a lightpen. This
is useful for owners of the original model A1000 and A2000s.
The joymouse.c example also shows how to change the first gameport to work
with a joystick, or remove it from the input food chain entirely so both
ports can be used for other purposes.
You need only disable the mouse to use the gameports for your special purposes.
You don't need to take over the whole machine. The gameport.device can then
be used to gather data from the port, or the hardware can be read directly.
Gameport Input
The gameport.device contains two units, one for each gameport. The original
Amiga design called for game controllers to be allocated to applications as
follows:
1. Let multiple opens of gameport.device occur.
2. Follow a high level protocol for determining if the
gameport is in use: check via AskCType for the flag
GPCT_NOCONTROLLER. If set, then no one else is using it.
There are some problems with this scheme. First, there is no protection
between SetCType and AskCType. So two tasks can AskCType, see NO, and
both SetCType. Second, because games often go directly to the hardware,
the gameport.device became just a spoon-feeder to the input.device for mouse
input.
Despite these problems, the gameports can still be shared between applications
in a system-compatible way. Neither the SetCType nor AskCType commands are
deferred, nor do they wait and thus break a Forbid. This means you can use
the following code to get a gameport:
Forbid
AskCType
if(GPCT_NOCONTROLLER){
SetCType
owned = TRUE }
else
owned = FALSE
endif
Permit
We normally try to discourage counting on this kind of side effect, but in
this case, it's the only way. If the port is being allocated, but not for
one of the supported controller types, then set the controller to the
catch-all value GPCT_ALLOCATED.
Joysticks
To read a joystick, there are three choices. The first is to change the
mouse port to a joystick port and capture events from the input device food
chain as a handler. This is not recommended.
The second choice is to read the joystick port directly from the hardware
registers. It's fairly easy to do and we cannot fault folks for doing it
this way - just make sure to allocate the gameport unit so other applications
know you've got the port. You should also allocate the POTGO resource bits
to acquire buttons 2 & 3.
The third choice is to use the gameport device. This polls every vertical
blank and only satisfies requests when trigger conditions are met. It's
advantages are that the polling runs out of the contention-free ROMs, and
when combined with certain reply port types (e.g. the direct call action),
can be efficient for your application.
Proportional Controller
Unfortunately, support for proportional controllers in the gameport device
was removed in one of the early code crunches. This occurred after support
for Coleco-style controllers had already been removed. The example program
propjoy.c shown below demonstrates how to use the potgo resource and some
vblank interrupt routines to track proportional controllers.
Keyboard Input
The input device IO requests to the gameport can be shut down but IO requests
to the keyboard cannot. If an application requests key events from the
keyboard, it will probably get about half of them: every other one going to
the input device. So, to learn about all the key states an application can
either poll the keyboard with the ReadMatrix command, or insert itself as a
handler in the food chain. If you use the ReadMatrix command, note that
io_Length MUST be exactly 13.
Other System Resources
----------------------
Misc
Whenever using serial or parallel port resources, they must be acquired from
the misc.resource. The misc.resource oversees usage of the serial data port,
the serial communication bits, the parallel data and handshake port, and the
parallel communication bits. The parallel communication bits double as the
Commodore serial bus interface bits for those who want to connect a 1541 to
the Amiga.
The serial and parallel devices both use the misc.resource. If the Exec
device is opened and closed, the appropriate two misc resources will be
allocated then freed. Unfortunately, if the DOS devices SER: or PAR: have
been opened, they will open the device and thus the corresponding resource
pair forever. For more information on this problem see the article on the
parallel and serial resources elsewhere in this issue.
Disk
Whenever using floppy disk resources, they must be acquired from the disk
resource. The disk resource provides both a gross and a fine unit allocation
scheme. AllocUnit and FreeUnit are used to claim a unit for long term use,
and GetUnit and GiveUnit are used to claim a unit for shorter periods.
The trackdisk.device uses and abides by both allocation schemes. Because a
trackdisk unit is never closed for Amiga 3.5" drives, since the file system
keeps them open, the associated resource units will always be allocated for
these drives. GetUnit and GiveUnit can still be used, however, by other
applications that have not succeeded with AllocUnit.
It is therefore possible to prevent the trackdisk device from using units that
have not been mounted yet by successfully performing an AllocUnit for that
unit. It is also possible to starve trackdisk usage by performing a GetUnit.
The appropriate companion routine (FreeUnit or GiveUnit) should be called to
restore the resource at the end of its use.
CIA
CIA resources not associated with the misc and disk resources are the joystick
fire buttons, which are acquired with the appropriate gameport.device unit,
the LED/audio filter, which is catch-as-catch-can, the memory overlay bit
and the keyboard serial interface, which is defined to be allocated by the
keyboard.device when it is running. The CIAB timers are available to be
allocated and are associated with an interrupt. It is therefore allocated by
successfully adding a vector for that interrupt with AddICRVector.
Amiga CIA Timer Allocation
CIAA (int 2)
timerA Used for keyboard handshake
timerB Used for uSec timer.device
TOD Used for 60Hz timer.device
CIAB (int 6)
timerA Commodore serial bus communication, usually not used
timerB Not used
TOD Used for graphics.library beam counter
By using the techniques outlined above, you can often avoid taking over the
whole system in your high-performance applications. Allocate only the
resources you need and return them when you are done.
Copyright (c) 1988 Commodore-Amiga, Inc.
Executables based on this information may be used in software
for Commodore Amiga computers. All other rights reserved.
This information is provided "as is"; no warranties are made.
All use is at your own risk, and no liability or responsibility is assumed.
;=============================================================
; makefile - requires Manx 3.6
;=============================================================
;
;joymouse: joymouse.o32 iefa.o
; ln joymouse.o32 iefa.o -lc32 -o joymouse
;
;joymouse.o32: joymouse.c
; cc +L +C +D +p -B -S -L100 -o joymouse.o32 joymouse.c
;
;iefa.o: iefa.asm
; as -o iefa.o iefa.asm
;
;=============================================================
; joymouse.c
;=============================================================
;
#define DEBUG
#include "exec/types.h"
#include "exec/nodes.h"
#include "exec/lists.h"
#include "exec/ports.h"
#include "exec/interrupts.h"
#include "devices/gameport.h"
#include "devices/input.h"
#include "devices/inputevent.h"
#include "libraries/dos.h"
struct Task *FindTask();
struct MsgPort iorp = {
{0, 0, NT_MSGPORT, 0, 0}, 0, SIGF_SINGLE, 0, {0, 0, 0, 0, 0}
};
struct IOStdReq ior = {
{{0, 0, NT_MESSAGE, 0, 0}, &iorp, 0},
0, 0, 0, 0, 0, 0, 0, 0, 0
};
struct InputEvent nullEvent = {
NULL, IECLASS_NULL
};
struct GamePortTrigger joyTrigger = {
GPTF_DOWNKEYS|GPTF_UPKEYS, 1, 1, 1
};
struct GamePortTrigger mouseTrigger = {
GPTF_DOWNKEYS|GPTF_UPKEYS, 0, 1, 1
};
extern VOID eventAFunction();
struct Interrupt inputHandler = {
{0, 0, NT_INTERRUPT, 100, 0}, 0, eventAFunction
};
#define MAXDELTA 255
#define DELTASHIFT 4
int deltaX = 0;
int deltaY = 0;
struct InputEvent *
eventFunction(event, data)
struct InputEvent *event;
{
struct InputEvent *loopevent;
#ifdef DEBUG
kprintf(".");
#endif
loopevent = event;
do {
if (loopevent->ie_Class == IECLASS_RAWMOUSE) {
/* handle X, then Y. slow down faster than speed up */
/* this could use another iteration to get it to "feel" right */
if (loopevent->ie_X < 0) {
#ifdef DEBUG
kprintf("<");
#endif
if (deltaX <= 0) {
if (deltaX > -MAXDELTA) deltaX--;
}
else deltaX = deltaX/2;
}
else if (loopevent->ie_X > 0) {
#ifdef DEBUG
kprintf(">");
#endif
if (deltaX >= 0) {
if (deltaX < MAXDELTA) deltaX++;
}
else deltaX = deltaX/2;
}
else if (loopevent->ie_Y != 0) {
if (deltaX != 0) deltaX += (deltaX<0)?1:-1;
}
else deltaX = deltaX/2;
if (deltaX < 0)
loopevent->ie_X = (deltaX-((1<<DELTASHIFT)+1))>>DELTASHIFT;
else
loopevent->ie_X = (deltaX+((1<<DELTASHIFT)-1))>>DELTASHIFT;
if (loopevent->ie_Y < 0) {
#ifdef DEBUG
kprintf("v");
#endif
if (deltaY <= 0) {
if (deltaY > -MAXDELTA) deltaY--;
}
else deltaY = deltaY/2;
}
else if (loopevent->ie_Y > 0) {
#ifdef DEBUG
kprintf("^");
#endif
if (deltaY >= 0) {
if (deltaY < MAXDELTA) deltaY++;
}
else deltaY = deltaY/2;
}
else if (loopevent->ie_X != 0) {
if (deltaY != 0) deltaY += (deltaY<0)?1:-1;
}
else deltaY = deltaY/2;
if (deltaY < 0)
loopevent->ie_Y = (deltaY-((1<<DELTASHIFT)+1))>>DELTASHIFT;
else
loopevent->ie_Y = (deltaY+((1<<DELTASHIFT)-1))>>DELTASHIFT;
}
}
while ((loopevent = loopevent->ie_NextEvent) != 0);
return(event);
}
main(argc, argv)
int argc;
char *argv[];
{
char c;
int result;
NewList(&iorp.mp_MsgList);
iorp.mp_SigTask = (struct Task *) FindTask((char *) NULL);
if (OpenDevice("input.device", 0, &ior, 0)) exit(20);
/* free this controller */
c = GPCT_NOCONTROLLER;
ior.io_Command = IND_SETMTYPE;
ior.io_Length = 1;
ior.io_Data = (APTR) &c;
DoIO(&ior);
/* wait for the SETM to take effect with a side effect */
ior.io_Command = IND_WRITEEVENT;
ior.io_Length = (LONG) sizeof(nullEvent);
ior.io_Data = (APTR) &nullEvent;
DoIO(&ior);
/* switch to the right game port */
c = 1;
ior.io_Command = IND_SETMPORT;
ior.io_Length = 1;
ior.io_Data = (APTR) &c;
DoIO(&ior);
/* set this controller to a joystick */
c = GPCT_ABSJOYSTICK;
ior.io_Command = IND_SETMTYPE;
ior.io_Length = 1;
ior.io_Data = (APTR) &c;
DoIO(&ior);
/* set the trigger for the joystick */
ior.io_Command = IND_SETMTRIG;
ior.io_Length = (LONG) sizeof(joyTrigger);
ior.io_Data = (APTR) &joyTrigger;
DoIO(&ior);
/* insert the accellerator handler into the food chain */
ior.io_Command = IND_ADDHANDLER;
ior.io_Data = (APTR) &inputHandler;
ior.io_Length = (LONG) sizeof(struct Interrupt);
DoIO(&ior);
/* wait for this program to terminate */
Wait(SIGBREAKF_CTRL_C);
/* free this controller */
c = GPCT_NOCONTROLLER;
ior.io_Command = IND_SETMTYPE;
ior.io_Length = 1;
ior.io_Data = (APTR) &c;
DoIO(&ior);
/* remove the accellerator handler from the food chain */
ior.io_Command = IND_REMHANDLER;
ior.io_Data = (APTR) &inputHandler;
ior.io_Length = (LONG) sizeof(struct Interrupt);
DoIO(&ior);
/* switch to the left game port */
c = 0;
ior.io_Command = IND_SETMPORT;
ior.io_Length = 1;
ior.io_Data = (APTR) &c;
result = DoIO(&ior);
/* set the trigger for a mouse */
ior.io_Command = IND_SETMTRIG;
ior.io_Length = sizeof(mouseTrigger);
ior.io_Data = (APTR) &mouseTrigger;
DoIO(&ior);
/* set this controller to a mouse */
c = GPCT_MOUSE;
ior.io_Command = IND_SETMTYPE;
ior.io_Length = 1;
ior.io_Data = (APTR) &c;
result = DoIO(&ior);
}
;=============================================================
; iefa.asm
;=============================================================
;
; Copyright (c) 1988 Commodore-Amiga, Inc.
;
; Executables based on this information may be used in software
; for Commodore Amiga computers. All other rights reserved.
;
; This information is provided "as is"; no warranties are made.
; All use is at your own risk, and no liability or responsibility is assumed.
;
XREF _eventFunction
XDEF _eventAFunction
_eventAFunction:
MOVEM.L A0/A1,-(A7)
JSR _eventFunction
ADDQ.L #8,A7
RTS
END
/******************************************************************************
*
* Source Control
* --------------
* $Header: propjoy.c,v 34.1 85/11/24 17:59:44 bart Exp $
*
* $Locker: $
*
* $Log: propjoy.c,v $
* Revision 34.1 85/11/24 17:59:44 bart
* be system compatible
*
* Revision 34.0 86/11/21 16:35:51 bart
* added to rcs for updating
*
* Copyright (c) 1988 Commodore-Amiga, Inc.
*
* Executables based on this information may be used in software
* for Commodore Amiga computers. All other rights reserved.
*
* This information is provided "as is"; no warranties are made.
* All use is at your own risk, and no liability or responsibility is assumed.
*
******************************************************************************/
/* main.c - test program for add/rem tof task - bart - 05.19.86 */
/* propjoy.c modified program for proportional controller - bart - 11.21.86 */
;=============================================================
; propjoy.c
;=============================================================
#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/execbase.h>
#include <exec/execname.h>
#include <graphics/gfxbase.h>
#include <graphics/graphint.h>
#include <hardware/cia.h>
#include <hardware/custom.h>
#include <hardware/intbits.h>
#include <resources/potgo.h>
#define V1_POINT_2 33
#define NUM_SERVERS 2
#define MAX_COUNT (UWORD)~1 /* do it for a while */
/* use system defined hard addresses */
extern struct Custom custom;
/* use system defined addresses for potgo and pot1dat */
#define POTGO &custom.potgo
#define POT0DAT &custom.pot0dat
#define POT1DAT &custom.pot1dat
/* vertical blank interrupt server priority */
#define HIGHINTPRI 127L /* needs to be replaced with priority relative */
#define LOWINTPRI -127L /* needs to be replaced with priority relative */
/* bit number defines for potgo ... */
#define START_B 0
#define DATRX_B 8
#define DATRY_B 10
#define DATLX_B 12
#define DATLY_B 14
/* masks ... */
#define START_F (1L << START_B)
#define DATRX_F (1L << DATRX_B)
#define DATRY_F (1L << DATRY_B)
#define DATLX_F (1L << DATLX_B)
#define DATLY_F (1L << DATLY_B)
#define RPOTX (START_F | DATRX_F)
#define RPOTY (START_F | DATRY_F)
#define RPOTXY (START_F | DATRX_F | DATRY_F)
#define LPOTX (START_F | DATLX_F)
#define LPOTY (START_F | DATLY_F)
#define LPOTXY (START_F | DATLX_F | DATLY_F)
struct ExecBase *ExecBase = NULL;
struct GfxBase *GfxBase = NULL;
struct PotgoBase *PotgoBase = NULL;
/* global storage for potdat */
UWORD oldbits = NULL;
UWORD potbits = NULL;
ULONG potdat = NULL;
/* create server-task to read the proportional joysticks,*/
/* update potdat, and then poke potgo to start next data read */
/* reserve space for the interrupt servers */
struct Isrvstr server[NUM_SERVERS] = {NULL};
first_server(i)
int i;
{
/* read previous proportional joystick values */
potdat = *(ULONG *)POT0DAT;
/* poke potgo, restore old bits */
WritePotgo(oldbits,((~1)<<8)|oldbits);
/* be nice -- let other servers run, too */
return(NULL);
}
second_server(i)
int i;
{
/* poke potgo, start prop joystick read */
WritePotgo(potbits,((~1)<<8)|potbits);
/* be nice -- let other servers run, too */
return(NULL);
}
main()
{
LONG error = FALSE;
struct Isrvstr *iserver[NUM_SERVERS];
LONG i;
/* set server priorities */
server[0].is_Node.ln_Pri = HIGHINTPRI;
server[1].is_Node.ln_Pri = LOWINTPRI;
/* set up server pointers */
iserver[0] = &server[0];
iserver[1] = &server[1];
if((ExecBase = (struct ExecBase *)OpenLibrary(EXECNAME,V1_POINT_2)) != NULL)
{
if((GfxBase = (struct GfxBase *)
OpenLibrary("graphics.library",V1_POINT_2)) != NULL)
{
if((PotgoBase = OpenResource(POTGONAME,V1_POINT_2)) != NULL)
{
/* remember currently used bits */
oldbits = ~(AllocPotBits(~1));
/* restore previous state of bit allocation */
FreePotBits(~oldbits);
/* now attempt to allocate start potbit */
potbits = AllocPotBits(START_F);
Forbid();
/* add interrupt servers */
AddTOF(iserver[0],first_server,0);
AddTOF(iserver[1],second_server,1);
Permit();
/* loop until done */
for(i=0; i < MAX_COUNT; i++)
{
/* wait for server to update pot values */
WaitTOF();
/* only output information once every second */
if(!(i % ExecBase->VBlankFrequency))
{
}
}
Forbid();
for(i=0; i < NUM_SERVERS; i++)
{
RemTOF(iserver[i]);
}
Permit();
/* free potbits */
FreePotBits(potbits);
CloseLibrary(GfxBase);
}
else
{
error = TRUE;
}
}
else
{
error = TRUE;
}
CloseLibrary(ExecBase);
}
else
{
error = TRUE;
}
/* return error code */
exit(error);
}