The WiPo mouse 99

This mouse was produded around 1986 by:

Softpoint Electronic, GmbH
Balanstr. 285 8000 Munchen 90
Germany

Hardware description

Software issues
CRU map
Mouse driver
Text mode


Hardware

The hardware consists in a small 6-chip adapter board that is plugged in the side port of the console, in between the console and the PE-Box cable connector. This design serves two functions: first the board can draw its power supply from the side port. Second, it can access the LOAD* line to issue non-maskable interrupts when the mouse is moved. An easily accessible switch allows the user to open this connection and to prevent interrupt from being sent when the mouse driver is not installed.

In addition, the board has a 9-wire connection cable to the joystick port. This enables the software to read the status of the two mouse buttons, and of the two motion detectors (left/right and up/down). The circuitery on board provides an extra signal that the software can use to determine whether the latest detected motion (that triggered the interrupt) was vertical or horizontal. These signals can be accessed by reading keyboard column 7, i.e. joystick 2.

Reading keyboard column 6 (joystick 1) temporarily prevents the emission of LOAD* interrupts. This avoids the re-entrancy problems due to two interrupts issued too close from each other (see my page on interrupts for more details). It also resets the vertical/horizontal flip-flop.

The mouse itself is a "no name" device made in Taiwan. It has a long cable with a 9-hole plug at the end, just like the joystick port. A corresponding male socket is provided on the adapter board.

      74LS121           74LS74    M3     74LS123
+------+ +------+ v +------+ 2K
J7>---|A1* R|-+5V +5V-|D | | Gnd-|A* CR|-+--WWW--+5V
+5V-|A2* | +5V-|Pr* | | | | = 333
+5V-|B C|-, | Clk|---+------|B C |-'
| | = 0.1uF | | | |
| CR|-' | | +5V-|Clr* |
| Q*|----+----|Clr* Q|--, | Q*|--,
+------+ | +------+ | +------+ | 74LS04
| | Gnd-|A* Q*|--==|)---|>o---, 74LS08 Switch
| | +5V-|B | 74LS08 ,==|)--|>o--/ o---> LOAD*
| | | C |-, | 74LS04
| | M1>---|Clr* | = 333 |
| | | CR|-+--WWW--+5V |
| | +------+ 2K |
'--------------|-----------------------------'
74LS367 |
+--------+ |
J2>----+--|1OE* 2A2|------------'
'--|2OE* 2Y2|----->J8 +5V----M7
| | Gnd----M8 ___________
M4>-------|1A1 2A1|-----<M2 nc --M5 ( 1 2 3 4 5 )
J9<-------|1Y1 2Y1|----->J3 \ 6 7 8 9 /
| |
M6>-------|1A2 1A4|-----<M9 nc --J1 M1-M9: Mouse plug
J4<-------|1Y2 1Y4|----->J3 nc --J6 J1-J9: Joystick port
+--------+

A rising edge on mouse connections M3 (vertical motion) and M1 (horizontal motion) triggers a short pulse in their respective half of the 74LS123 one-shot. The two Q* outputs are combined via an AND gate so that, if at least one pulse is occuring, the LOAD* line will be low. The second AND gate is used to mask out this signal: when joystick 1 is accessed, it triggers a longer pulse on the 74LS121 one-shot. The resulting low signal on the Q* output prevents the LOAD* line from going low, no matter what happened to the mouse.

Accessing joystick 2 enables the 74LS373 tri-state buffer and allows for reading the four mouse signals through the joystick return lines. The fifth line is connected to the Q output of a 74LS74 flip-flop: it is used to determine whether a vertical motion was detected or not. A rising edge on M3 brings the Q output high, it can only be reset by accessing joystick 1.


Software

The mouse came with a companion disk containing completely useless demo software and very meager explanations on how to write a mouse driver. I had to experiment quite a bit in order to figure out how to access the mouse!

CRU map

R12 address Bit Meaning
>0006 3 Left button. 0: down 1: up
>0008 4 Right button. 0: down 1: up
>000A 5 Vertical motion. 0: down 1:up
>000C 6 Motion detector. 0: horizontal 1: vertical
>000E 7 Horizontal motion. 0: right 1: left
>0024
>0026
>0028
18
19
20
1 1 1: read mouse values in bits 3-7
0 1 1: temporarily disable LOAD* interrupts
and reset bit 6.


Mouse driver

The software must hook the non-maskable interrupt vector at >FFFC-FFFF to trap any interrupt generated by the mouse. The interrupt service routine can then read CRU bits 4-6 to determine what kind of motion caused the interrupt and update the vertical and horizontal position buffers.

The value of these counters can then be used by the program to display a mouse cursor on screen. A good way to do this is to hook the VDP interrupts that occur 60 times per second (50 in Europe) and update the position of a sprite according to the moves of the mouse.

*======================================================================
* Demo driver for the WiPo mouse 99
* ---------------------------------
* It moves a sprite on screen according to the moves of the mouse
* (without checking for screen boundaries).
* The left mouse button changes the sprite pattern (crosshair/arrow).
* The right button changes the sprite color (black/white).
*======================================================================
DEF START
*
START LI R0,>0400 initialize sprite patterns
LI R1,PATS
LI R2,16 2 chars: crosshair and arrow
BL @VMBW
LI R0,>0300 initialize sprite 0 attributes
LI R1,SPRITE
LI R2,5
BL @VMBW

LI R0,INTER hook the VDP interrupt routine
MOV R0,@>83C4

CLR @WREGS+18 reset motion counters (R9 and R10 in WREGS)
CLR @WREGS+20

MOV @YSWR,@>FFFC hook the LOAD* interrupt vectors
MOV @YSPG,@>FFFE

EVER LIMI 2 enable VDP interrupts
LIMI 0 disable them
JMP EVER wait forever (Fctn= is active)

*-------------------------------------------
* Unmaskable LOAD* interrupt service routine
*-------------------------------------------
CHECK MOV @NOWR,@>FFFC change WS to allow 1 re-entrancy
LIMI 0 disable VDP interrupts
LWPI WREGS our workspace
MOV @NOPG,@>FFFE prevents re-entrancy (this points to a RTWP)

CLR R12 CRU address of the TMS9901
SBO 18 keyboard column 7 (joystick 2) to read mouse param
SBO 19
SBO 20

TB 6 vertical or horizontal interrupt?
JNE SK1 horizontal
INC R9 vertical: increment counter
TB 5 up or down?
JEQ SK2 up: we are done
DECT R9 down: we should have decremented the counter
JMP SK2

SK1 INC R10 horizontal motion: increment the counter
TB 7 left or right?
JEQ SK2 right: we are done
DECT R10 left: we should have decremented the counter

SK2 SBZ 18 reset bit 6, disable LOAD* interrupts for a while
MOV @YSWR,@>FFFC restore the LOAD* vectors
MOV @YSPG,@>FFFE no interrupt should occur here!
RET RTWP

*-------------------------------------------
* Display routine, hooked to the VDP interrupt service routine
*-------------------------------------------
INTER LIMI 0 disable interrupts (just in case)
LWPI WREGS our workspace
LI R0,>0300 read sprite 0 attributes
LI R1,BUF
LI R2,4
BL @VMBR1

LI R1,BUF update the position
SLA R9,8 to slow the sprite: SLA R9,7 To speed it up: SLA R9,9
AB R9,*R1+ add the vertical motion
SLA R10,8
AB R10,*R1+ add the horizontal motion

CLR R12 CRU address of the TMS9901
SBO 18 keyboard column 7: access mouse params
SBO 19
SBO 20

LI R0,>8000 sprite pattern # 128 (crosshair)
TB 3 test left button
JEQ SK3 up
AI R0,>0100 down: change for pattern # 129 (arrow)
SK3 MOVB R0,*R1+ write it back

LI R0,>0100 color = black
TB 4 test right button
JEQ SK4 up
LI R0,>0F00 down: change color to white
SK4 MOVB R0,*1+ write it back

SBZ 18 reset bit 6, temporarily disable LOAD*
LI R0,>0300 write back sprite parameters
LI R1,BUF
LI R2,4
BL @VMBW1

CLR R9 reset counters
CLR R10
LWPI >83E0 back to interrupt service routine workspace
B *R11 return to it

*-------------------------------------------
* Subroutines used by the above
*-------------------------------------------
VMBR1 SWPB R0 read several bytes from the VDP
MOVB R0,@>8C02 -------------------------------
SWPB R0 address in R0
MOVB R0,@>8C02
LP3 MOVB @>8800,*R1+ data pointer in R1
DEC R2 number of bytes in R2
JH LP3
B *R11
*
VMBW1 ORI R0,>4000 write several bytes to the VDP
SWPB R0 ------------------------------
MOVB R0,@>8C02 address in R0
SWPB R0
MOVB R0,@>8C02
LP2 MOVB *R1+,@>8C00 pointer in R1
DEC R2 number of bytes in R2
JH LP2
B *R11

*-------------------------------------------
* Data
*-------------------------------------------
WREGS BSS 18 our main workspace
WREG1 DATA 0,0 temporary workspace for re-entrancy
BSS 28
BUF BSS 4 buffer for sprite data

YSWR DATA WREGS LOAD* interrupt vectors
YSPG DATA CHECK
NOWR DATA WREG1 vectors in use while processing the LOAD* interrupt
NOPG DATA RET
SPRITE DATA >6080,>8001,>D000 sprite 0 attributes
PATS DATA >1010,>10FE,>1010,>1000 crosshair pattern
DATA >0000,>001E,>1814,>1201 arrow pattern

END


Text mode

Things are more complicated in text mode, as no sprite can be displayed. We must therefore superimpose the mouse cursor to the pattern of the characters it overlaps. This implies to

  • Determine the position of the cursor in terms of characters.
  • Read a 2x2 characters box around this position.
  • Read the patterns of these chars into a buffer.
  • Superimpose the cursor pattern after due shifting up/down and left/right to point to the correct pixel.
  • Assign these patterns to 4 unused characters (e.g. characters 26 to 29).
  • Write these 4 characters into the 2x2 box.
  • When the cursor moves, restore the initial 2x2 characters and repeat the whole process.

  • *=====================================================================
    * Demo on how to displays a cursor pattern in text mode
    * Assume SPROW and SPCOL were set by a routine similar to INTER above
    *=====================================================================

    * These routines set the VDP address for a write/read respectively
    VWR ORI R0,>4000 address in R0
    VRD SWPB R0
    LI R14,>8C02
    MOVB R0,*R14 set address
    SWPB R0
    MOVB R0,*R14
    DECT R14 R14 is >8C00: VDP write port
    LI R13,>8800 R13 is >8800: VDP read port
    B *R11

    * This routine reads several bytes from the VDP
    VMBR1 MOV R11,R8 address in R0
    BL @VRD
    LP3 MOVB *R13,*R1+ data pointer in R1
    DEC R2 number of bytes in R2
    JH LP3
    B *R8

    * This routine writes several bytes to the VDP
    VMBW1 MOV R11,R8 address in R0
    BL @VWR
    LP2 MOVB *R1+,*R14 data pointer in R1
    DEC R2 number of bytes in R2
    JH LP2
    B *R8

    * This routine saves a character definition into a buffer
    * Input: R0msb = char number
    SAVPAT SRL R0,8
    SLA R0,3 8 bytes per char
    AI R0,>0800 address of pattern table
    LI R2,8
    JMP  VMBR1

    * This routine calculates the screen position of the mouse cursor
    * Inputs: SPROW = pixel row, SPCOL = pixel column
    * Outputs: R0 = screen address (0-3BF), R1 = char column (0-39)
    FPOS MOV @SPROW,R1 get pixel row
    SRL R1,3 chars are 8-pixel high: divide by 8
    MPY @H0028,R1 40 columns in text mode
    MOV @SPCOL,R1 get pixel column
    CLR R0
    DIV @H0006,R0 characters are 6-pixel wide
    MOV R0,R1 R1 = character column
    A R2,R0
    MOV R0,@SCPOS R0 = screen address
    B *11

    * This routine sets a 2x2 character box at the cursor position
    * Inputs: R0 = screen address, R1 = 4-char buffer
    PUT4CH MOV R11,R10 save return address
    BL @VWR set VDP to write
    MOVB *R1+,*R14 write 2 chars
    MOVB *R1+,*R14
    AI R0,40 next line
    BL @VWR set VDP to write
    MOVB *R1+,*R14 write two chars
    MOVB *R1+,*R14
    SKRD B *R2
    * This routine reads a 2x2 character box at the cursor position
    * Inputs: R0 = screen address, R1 = 4-char buffer
    GET4CH MOV R11,R10
    LI 1,BUF1 read 4 chars
    BL @VRD set VDP to read
    MOVB *13,*1+ read two chars
    MOVB *13,*1+
    AI 0,40 next line
    BL @VRD set VDP to read
    MOVB *13,*1+ read 2 chars
    MOVB *13,*1+
    B *R10
    * This routine displays the cursor pattern on screen
    * Inputs: pixel row in SPROW, pixel column in SPCOL
    PUTCUR MOV R11,R9 save return point
    BL @NOCUR erase existing cursor, if any
    BL @FPOS find new cursor position
    BL @GET4CH read a 2x2 box into a buffer
    LI R3,BUF1 copy char patterns into another buffer
    MOVB *R3,R0 get char 1
    LI R1,BUF2 pattern buffer
    BL @SAVPAT save char pattern
    MOVB @2(R3),R0 ditto for char 3 (watch the order!)
    BL @SAVPAT
    MOVB @1(R3),R0 ditto for char 2
    BL @SAVPAT
    MOVB @3(R3),R0 ditto for char 4
    BL @SAVPAT

    MOV @SPCOL,R1 Now add cursor pattern. Get pixel column
    CLR R0
    DIV @H0006,R0 chars are 6-pixels wide
    MOV R1,R0 pixel column inside character
    MOV @SPROW,R1 get pixel row
    ANDI R1,>0007 pixel line in character
    AI R1,BUF2+16 pattern for char 2, at appropriate pixel line
    LI R2,PATRN cursor pattern
    LI R3,8 cursor is 8-pixel high
    LHH0 CLR R5
    MOVB *R2+,R5 get 1 line from the cursor pattern
    SRC R5,0 shift it to begin at appropriate pixel column
    SOCB R5,@-16(R1) superimpose it on char 1 (or 3)
    SLA R5,6 the remainder will be on char 2 (or 4)
    SOCB R5,*R1+ superimpose it on char 2 (or 4)
    DEC R3
    JNE LHH0

    LI R0,>08D0 address of pattern for char 26-29
    LI R1,BUF2
    LI R2,32
    BL @VMBW1 set their patterns
    MOV @SCPOS,R0 display char 26-29 at cursor position
    LI R1,SPCH
    BL @PUT4CH
    B *R9
    * This routine erases the cursor displayed by the above
    NOCUR MOV R11,R9
    MOV SCPOS,R0 screen position of the cursor
    LI R1,BUF1 buffer where the 4 chars were saved
    MOV *R1,*R1 but were they?
    JEQ SK1 not yet: no cursor on screen
    BL @PUT4CH restore them
    CLR @BUF1 flag: no cursor on screen
    SK1 B *R9
    SCPOS  DATA 0             current screen address of the cursor
    SPROW DATA 0 pixel row for the cursor
    SPCOL DATA 0 pixel column for the cursor
    BUF1 DATA 0,0 buffer for 4 chars
    BUF2 BSS 32 buffer for 4 char patterns
    SPCH   BYTE 26,28,27,29   characters that make up the cursor
    PATRN DATA >2020,>F820,>2000,>0000 cursor pattern
    H0028 DATA >0028 constant: 40 columns
    H0006 DATA >0006 constant: 6 pixels

    Revision 1. 6/20/99. Ok to release

    Back to the TI-99/4A Tech Pages