Does anyone have any disassemblies of code for reading the mouse and button
status?
If you do and could post it with a walkthrough of what is happening, I'd be
very appreciative.
I'm aiming to add mouse control to a few programs I'm working on. For the
life of me, I can't
figure out how to do it in machine language. Ultimately I would like to
have keyboard and
mouse control running at the same time, so would I want to use
transparent-mode or interrupt mode?
Cheers,
Craig
>Setmouse, Clampmouse, Initmouse... ?! Argh!
>Does anyone have any disassemblies of code for reading the mouse and button
>status? If you do and could post it with a walkthrough of what is
>happening, I'd be very appreciative.
Hi Craig,
A long time ago I disassembled the Apple II mouse routines. Some of the
results of this effort are incorporated into various Aztec C programs and
are available from the Aztec C Museum. The AppleX distribution contains a
mouse sample but the MeToo project is the best implementation:
AppleX ProDOS OVERLAY Application Project - "Me Too! - Mois Aussi!"
http://www.aztecmuseum.ca/metoox.zip
ReadMe - Programmer
http://www.aztecmuseum.ca/docs/metoox.txt
"Me Too! - Moi Aussi!" ProDOS 8 (User Versions - English and French)
http://www.appleoldies.ca/metoo.zip
"Me Too!" English User Manual
http://www.appleoldies.ca/MeToo.pdf
"Moi Aussi!" French User Manual
http://www.appleoldies.ca/MoiAussi.pdf
I don't generally code by disassembly however so most of my work is properly
programmed from the ground-up.
As an aside, for those uniniated in the use of overlays, this was the
industry standard many moons ago and I used the technique quite extensively
on these small olde computers. The MeToo project is probably the best
example overlay project that I can provide for the Apple //e.
I will post code snippets for this separately. I should note that Aztec C's
implementation of inline assembly is extremely robust and consistent across
many platforms. Variations were later adopted by Microsoft C and others.
Have Fun!
Bill Buckels
August 2009
>I will post code snippets for this separately.
Taken from MeToo:
http://www.aztecmuseum.ca/metoox.zip
/* mouse routines */
/* turn the mouse on */
mouseon()
{
setmouseon();
homemouse();
readmouse();
}
/* read the mouse */
/* update the globals */
cmouse()
{
unsigned char *ptr;
readmouse();
/* mouse status register */
ptr=(unsigned char *)1916;
mousebutton=0;
mousebutton+=ptr[0];
/* x coordinate - units = 0-1023 */
mousex=0;
ptr=(unsigned char *)1404; /* MSB */
mousex +=ptr[0];
mousex <<= 8;
ptr=(unsigned char *)1148; /* LSB */
mousex +=ptr[0];
if(mousex>MXMAX)mousex=MXMAX;
/* use 0-1023 horizontal units */
/* y coordinate - units = 0-1023 */
mousey=0;
ptr=(unsigned char *)1532; /* MSB */
mousey +=ptr[0];
mousey <<= 8;
ptr=(unsigned char *)1276; /* LSB */
mousey +=ptr[0];
if(mousey>MYMAX)mousey=MYMAX;
/* use 0-1023 vertical units */
}
/* 6502 assembly language mouse core functions */
#asm
*
* use the second line of the second page of the text screen
* as a dynamic dispatch vector set pointing to the mouse routines
*
SETMOUSE EQU $880
SERVEMOUSE EQU SETMOUSE+3
READMOUSE EQU SETMOUSE+6
CLEARMOUSE EQU SETMOUSE+9
POSMOUSE EQU SETMOUSE+12
CLAMPMOUSE EQU SETMOUSE+15
HOMEMOUSE EQU SETMOUSE+18
INITMOUSE EQU SETMOUSE+21
public setmouseon_
setmouseon_
lda #$01
ldx #$C4
ldy #$40
cli
jsr SETMOUSE
rts
public homemouse_
homemouse_
ldx #$C4
ldy #$40
jsr HOMEMOUSE
rts
public readmouse_
readmouse_
ldx #$C4
ldy #$40
php
sei
jsr READMOUSE
cli
plp
rts
public mouseoff_
mouseoff_
lda #$00
ldx #$C4
ldy #$40
cli
jsr SETMOUSE
rts
#endasm
/* as above noted:
* use text screen page two line two as an intermediate jump area
* for access to the 8-mouse firmware subroutines.
* we use a jsr to get there from our code.
* our jmp vector instruction is placed there based on the rom
* configuration of the current platform.
* because we jsr'd from our code, we will return safely to our
* code. this is why we use a jmp to loop from our vector
* to the firmware.
*/
mouseinit()
{
unsigned char *vptr,*tptr;
unsigned char temp,i;
vptr=(unsigned char *)0xc40b; /* firmware card signature */
temp=vptr[0];
if(temp!=1)return; /* this must be set to 1 */
temp=vptr[1]; /* X Y pointing device signature */
if(temp!=32)return; /* this must be set to $20 */
vptr=(unsigned char *)0xc411; /* start of mouse vectors */
mouseflag=vptr[0]; /* this must be set to 0 */
if(mouseflag)return;
tptr=(unsigned char *)0x880; /* base address - page 2 line 2 */
for(i=0;i<8;i++)
{
*vptr++;
temp=*vptr;
*tptr= '\x4c'; /* raw jmp instruction */
*tptr++;
*tptr= temp ; /* mouse subroutine offset */
*tptr++;
*tptr= '\xc4'; /* base of mouse routines */
*tptr++;
}
}
>The AppleX distribution contains a mouse sample...
The following code may prove too much for the faint of heart and therefore I
warn you ahead of time that the mouse implementation in the MeToo project
may be more concise without so much information.
Shazbut! And remember that working samples and makefiles are also provided
with this compiler which comes with an assembler as well as offering support
for inline assembly:
AppleX ProDOS Distribution rebundled for Windows XP
http://www.aztecmuseum.ca/AppleX.zip
ReadMe
http://www.aztecmuseum.ca/docs/AppleX.txt
Code Follows:
x--- snip ---x
/* amouse.c by bill buckels 1993 */
/* mouse prototype for the apple */
/* written in aztec C */
/* uses relocatable mouse calls in hope of avoiding discrepancies */
/* between the //e, IIc, GS, and other possibly different firmware */
/* notably all 3 computers treat the mouse as a slot 4 device */
/* when running under prodos 8. */
/* also notably there is no way to tell if the mouse is unplugged */
/* on the //e since the firmware does not test for the presence */
/* of a working mouse before installing itself. */
/* on the //e there appears to be no way to set the mouse boundaries */
/* although these routines are documented on the IIc. */
/* this has been confirmed in both Applesoft BASIC and Aztec C. */
/* see mouseloop.bas below. */
/* it is for this reason that a clipping scheme is implemented. */
/*
1 'MOUSELOOP.BAS test for APPLE //e MOUSE
10 D$ = CHR$ (4)
20 PRINT D$;"PR#4": PRINT CHR$ (1)
30 PRINT D$;"PR#0"
40 PRINT D$;"IN#4"
45 POKE 1661,39: POKE 1917,0
46 POKE 1789,23: POKE 2045,0
50 HOME
60 INPUT "";X,Y,S
70 HTAB 1: VTAB 8
80 PRINT X SPC( 1),Y SPC( 1),S SPC( 1)
90 GOTO 60
*/
/* if mouse boundaries can be set, the mouse position can be */
/* controlled by setting the minimum coordinates to the desired */
/* position and then by calling the HOMEMOUSE firmware routine. */
/* after the mouse is repositioned the minimum coordinates can */
/* be reset to 0,0 to allow normal movement. */
/* unfortunately this scheme is not workable. */
/* there appears no way to reposition the mouse except to 0,0 */
/* and for this reason the mouse should likely not be used as */
/* a pointing device if an arrow key is to be also used to move */
/* a cursor about the screen, since a mouse position over-ride */
/* is required during simultaneous use but not apparently possible.*/
/* Either can be used to point but not both on the same menu. */
/* A solution to this is to allow the use of the ARROW KEYS */
/* to disable the use of the mouse as pointing device. */
/* at each module change both the ARROW KEYS and MOUSE are active. */
/* (if the computer has a mouse). */
/* if the user uses the mouse to move the cursor, the arrow keys must */
/* be left alone or they will turn the mouse off. */
/* doing this allows the user to choose his method of directional */
/* navigation. Restarting the module turns the mouse on again. */
/* this option does not affect menus that specifically require */
/* the exclusive use of a particular directional device. */
/* (i.e. a mouse-use tutorial cannot be done with arrow keys */
/* nor can an arrow key-use tutorial be done with a mouse.) */
/*
Additional Notes: 2007
http://home.swbell.net/rubywand/csa2pfaq.html
How do I write programs which use the mouse?
The assembly language interface to the mouse firmware is documented in three
places:
- the reference material that was supplied with the AppleMouse card for the
IIe.
- the IIc Technical Reference Manual.
- the IIgs Firmware Reference Manual.
Interfacing to the mouse is somewhat complicated, especially if you want to
implement
some kind of mouse cursor (usually requires writing an interrupt handler).
The first problem is locating the mouse firmware. It could be in any slot
for a IIe or IIgs,
or either of two slots for the IIc. The safest method is just to do a slot
search, looking
for the mouse ID bytes:
$Cn05 = $38
$Cn07 = $18
$Cn0B = $01
$Cn0C = $20
$CnFB = $D6
On a ROM 3 IIgs, it is possible that the mouse firmware will not be
available, because
this doesn't prevent the use of the mouse from GS/OS (the Miscellaneous
Toolset or
Event Manager can still be used). An AppleMouse card, if installed, is not
used by the toolbox.
On a ROM 1 IIgs, the slot mouse firmware is used by the toolbox, so slot 4
must be set
to "Mouse Port", or an AppleMouse card may be installed in any slot.
Using the mouse firmware consists of calling the various parameters provided
by the
firmware. An entry point table is provided in the mouse slot. The routines
common
to all implementations are:
$Cn12 SETMOUSE Sets mouse mode
$Cn13 SERVEMOUSE Services mouse interrupt
$Cn14 READMOUSE Reads mouse position
$Cn15 CLEARMOUSE Clears mouse position to 0 (for delta mode)
$Cn16 POSMOUSE Sets mouse position to a user-defined pos
$Cn17 CLAMPMOUSE Sets mouse bounds in a window
$Cn18 HOMEMOUSE Sets mouse to upper-left corner of clamp win
$Cn19 INITMOUSE Resets mouse clamps to default values;
sets mouse position to 0,0
Each of the above locations contains the low byte of the entry point for the
routine.
The high byte is $Cn. The usual method for calling these routines is to set
up a single
subroutine which is patched with the location of the appropriate routine as
required.
You could also set up a series of subroutines for calling each routine.
The general logic would be as follows:
Locate the mouse slot by searching for the ID bytes described earlier.
Patch the slot into the following routine:
TOMOUSE LDX #$C1 ; Patch operand byte with slot in $Cn form
LDY #$10 ; Patch operand byte with slot in $n0 form
JMP $C100 ; Patch high byte of operand with slot in
; $Cn form. Low byte of operand must be
; patched with entry point from table above
You should also set up a pair of locations on zero page containing $Cn00,
which will be used
to look up the table. You can then have code as follows to call each of the
routines:
MOUSEPTR EQU $00 ; (or some other pair of zero page locations)
SETMOUSE
LDY #$12 ; Offset to entry point
BNE GOMOUSE ; Go to the mouse routine - always taken
SERVEMOUSE
LDY #$13 ; Offset to entry point
BNE GOMOUSE ; Go to the mouse routine - always taken
[etc. - one routine for each mouse call you will be using]
GOMOUSE TAX ; Preserve the value in A
LDA (MOUSEPTR),Y ; Get the routine entry point
STA TOMOUSE+5 ; Patch the JMP instruction
TXA ; Restore the value in A
; The following operand bytes must be patched by the
; initialization code which detects the mouse.
TOMOUSE LDX #$C1 ; Set up slot in $Cn form in X
LDY #$10 ; Set up slot in $n0 form in Y
JMP $C100 ; Go to the mouse routine
With code like the above, your program can just use JSR INITMOUSE, etc. to
call the
appropriate routine.
The mouse routines make use of screen holes for the slot containing the
mouse interface
firmware/card. The screen holes are as follows:
$0478 + slot Low byte of absolute X position
$04F8 + slot Low byte of absolute Y position
$0578 + slot High byte of absolute X position
$05F8 + slot High byte of absolute Y position
$0678 + slot Reserved and used by the firmware
$06F8 + slot Reserved and used by the firmware
$0778 + slot Button 0/1 interrupt status byte
$07F8 + slot Mode byte
You can access the screen holes by getting the mouse slot number in the $Cn
form (LDX TOMOUSE+1),
then indexing off the above locations minus $C0 with X (or just AND the
value with $0F and
use the base addresses directly).
The screen holes should be used only as specified by the mouse routines
below. You should never
write to them, except as specified by POSMOUSE.
The interrupt status byte is defined as follows:
Bit 7 6 5 4 3 2 1 0
| | | | | | | |
| | | | | | | \--- Previously, button 1 was up (0) or down (1)
| | | | | | \----- Movement interrupt
| | | | | \------- Button 0/1 interrupt
| | | | \--------- VBL interrupt
| | | \----------- Currently, button 1 is up (0) or down (1)
| | \------------- X/Y moved since last READMOUSE
| \--------------- Previously, button 0 was up (0) or down (1)
\----------------- Currently, button 0 is up (0) or down (1)
(Button 1 is not physically present on the mouse, and is probably
only supported for an ADB mouse on the IIgs.)
The mode byte is defined as follows.
Bit 7 6 5 4 3 2 1 0
| | | | | | | |
| | | | | | | \--- Mouse off (0) or on (1)
| | | | | | \----- Interrupt if mouse is moved
| | | | | \------- Interrupt if button is pressed
| | | | \--------- Interrupt on VBL
| | | \----------- Reserved
| | \------------- Reserved
| \--------------- Reserved
\----------------- Reserved
The button and movement status are only valid after calling READMOUSE.
Interrupt status bits
are only valid after SERVEMOUSE and are cleared by READMOUSE. The
appropriate screen hole information must be copied elsewhere before enabling
interrupts with CLI or PLP.
The routines are used as follows. X and Y must be set up with $Cn and $n0 in
all cases.
Interrupts must be disabled before calling any of these routines. Assume all
registers are
scrambled on exit unless otherwise noted.
SETMOUSE
Sets mouse operation mode.
Entry: A = mouse operation mode ($00 to $0F) - see mode byte.
Exit: C = 1 if illegal mode entered.
Screen hole mode byte is updated.
SERVEMOUSE
Tests for interrupt from mouse and resets mouse's
interrupt line.
Exit: C = 0 if mouse interrupt occurred.
Screen hole interrupt status bits are updated to show
current status.
READMOUSE
Reads delta (X/Y) positions, updates absolute X/Y pos,
and reads button statuses from the mouse.
Exit: C = 0 (always).
Screen hole positions and button/movement status bits are
updated, interrupt status bits are cleared.
CLEARMOUSE
Resets buttons, movement and interrupt status 0.
(This routine is intended to be used for delta mouse
positioning instead of absolute positioning.)
Exit: C = 0 (always).
Screen hole positions and button/movement status bits are
updated, interrupt status bits are cleared.
POSMOUSE
Allows caller to change current mouse position.
Entry: Caller places new absolute X/Y positions directly in
appropriate screen holes.
Exit: C = 0 (always).
Screen hole positions may be updated if necessary (e.g.
clamping).
CLAMPMOUSE
Sets up clamping window for mouse user. Power up default
values are 0 to 1023 ($0000 to $03FF).
Entry: A = 0 if entering X clamps, 1 if entering Y clamps.
Clamps are entered in slot 0 screen holes as follows.
NOTE: these are NOT indexed by the mouse slot number.
$0478 = low byte of low clamp.
$04F8 = low byte of high clamp.
$0578 = high byte of low clamp.
$05F8 = high byte of high clamp.
Exit: C = 0 (always).
Screen hole position is set to top left corner of clamping
window for the IIgs.
Note: The IIgs automatically homes the mouse when this call is
made, but this doesn't happen for the IIc and AppleMouse
card.
HOMEMOUSE
Sets the absolute position to upper-left corner of
clamping window.
Exit: C = 0 (always).
Screen hole positions are updated.
INITMOUSE
Sets screen holes to default values and sets clamping win
to default value of 0 to 1023 in both X and Y directions,
resets hardware.
Exit: C = 0 (always).
Screen holes are updated.
The general method of using the mouse firmware is as follows:
- Call SETMOUSE specifying a mode of 1 (enabled, no interrupts).
- Call INITMOUSE.
- Call CLAMPMOUSE to set up the required clamps (once per ea axis).
- If necessary, call SETMOUSE again with the actual mode you want.
You must set up a ProDOS interrupt handler if you want to use the
interrupt modes.
If you are using polled mode, call READMOUSE as required to update the mouse
position
and button status information.
If you are using interrupt mode, your interrupt handler should call
SERVEMOUSE to check
for a mouse interrupt. If none occurred, return to ProDOS with C=1.
If one did occur, note the type of interrupt (if necessary), then call
READMOUSE to
the new position information, and copy the data elsewhere. Finally, return
to ProDOS with C=0.
When your program is finished, it should disable the mouse by using SETMOUSE
with A=0, and
remove the interrupt handler (if necessary). --David Empson
*/
#include <stdio.h>
#include <prodos.h>
#include <sgtty.h>
#include <device.h>
#include <sysfunc.h>
#include <console.h>
#define REVERSE_VIDEO 0
#define NORMAL_VIDEO 128
/* a global flag for program management */
/* must be set to 0 if mouse exists */
unsigned char mouseflag=1;
/* global mouse counters */
int mousebutton,mousex,mousey;
#define XMAX 39
#define YMAX 23
main()
{
int oldx= 0, oldy=0, mousestatus;
scr_clear();
mouseinit();
if(mouseflag)
{
DML("NO MOUSE! Press any key...",10,10);
getch();
scr_clear();
_exit();
}
mouseon();
DMC(' ',mousey,mousex,REVERSE_VIDEO);
do{
cmouse();
if(oldx!=mousex || oldy!= mousey)
{
DMC(' ',oldy,oldx,NORMAL_VIDEO);
DMC(' ',mousey,mousex,REVERSE_VIDEO);
oldx=mousex;
oldy=mousey;
}
mousestatus=mousebutton>>7;
}while(mousestatus!=1);
mouseoff();
scr_clear();
_exit();
}
/* turn the mouse on */
mouseon()
{
setmouseon();
homemouse();
readmouse();
}
/* read the mouse */
cmouse()
{
unsigned char *ptr;
readmouse();
/* mouse status register */
ptr=(unsigned char *)1916;
mousebutton=0;
mousebutton+=ptr[0];
/* x coordinate - units = 0-1023 */
mousex=0;
ptr=(unsigned char *)1404; /* MSB */
mousex +=ptr[0];
mousex <<= 8;
ptr=(unsigned char *)1148; /* LSB */
mousex +=ptr[0];
if(mousex>XMAX)mousex=XMAX;
/* use 0-39 horizontal units */
/* y coordinate - units = 0-1023 */
mousey=0;
ptr=(unsigned char *)1532; /* MSB */
mousey +=ptr[0];
mousey <<= 8;
ptr=(unsigned char *)1276; /* LSB */
mousey +=ptr[0];
if(mousey>YMAX)mousey=YMAX;
/* use 0-23 vertical units */
}
public homemouse_
public mouseoff_
mouseoff_
#endasm
}
/* base addresses for each text line */
/* page one of text screen */
int textbase[24]={
0x0400,
0x0480,
0x0500,
0x0580,
0x0600,
0x0680,
0x0700,
0x0780,
0x0428,
0x04A8,
0x0528,
0x05A8,
0x0628,
0x06A8,
0x0728,
0x07A8,
0x0450,
0x04D0,
0x0550,
0x05D0,
0x0650,
0x06D0,
0x0750,
0x07D0};
DML(str,row,col)
char *str;
int row, col;
{
int len=strlen(str);
char *crt = (char *)(textbase[row]+col);
char c;
while((c=*str++)!=0)
*crt++=(c+NORMAL_VIDEO);
}
/* DMC = Direct Memory Character */
DMC(BYTE,row,col,cooked)
char BYTE;
int row, col,cooked;
{
char *crt = (char *)(textbase[row]+col);
if(BYTE >= 'a' && BYTE <= 'z')cooked = NORMAL_VIDEO;
if(!(cooked))
{
if(BYTE >= 'A' && BYTE <= 'Z')BYTE-=64;
}
else BYTE+=cooked;
if(col<40)*crt=BYTE;
}
Hi Craig-
I use the mouse calls extensively in VM02. You can look at these
files:
http://vm02.cvs.sourceforge.net/viewvc/vm02/vm02/src/mousedrvr.s?view=markup
and
http://vm02.cvs.sourceforge.net/viewvc/vm02/vm02/src/apple2/Mouse.java?view=markup
but they aren't as clear as Bill's examples. However, I might be able
to address the transparent vs. interrupt modes. Depending on how you
want to use the mouse input, and how much work you are willing to do
will dictate the approach. The interrupt mode gives your application
the chance to update the mouse position during VBL for a smooth,
flicker free cursor. This comes with a lot more work on your part to
manage the interrupts carefully (and deal with the slight differences
between the II+, IIe, IIc, and IIgs). If your app has a tight input
loop and flicker isn't a concern, just polling for the mouse position
and status in transparent mode, then updating the cursor will
suffice. Note that these modes are actually quite similar -
transparent mode just fills in the interrupt handler with the default
status update code in the mouse firmware. In both cases, you call the
firmware to copy its internal status values to the slot screen holes
where you read them from your app. I would recommend starting with
transparent mode, then hooking the interrupts later on if needed.
Another thing to be aware of is the mouse VBL detection process that
occurs in a II and II+. The mouse firmware clears HGR1 to detect
onset of VBL. If you have any code or data loaded there when you init
the mouse, it might cause you some pain and consternation.
Dave...