2025-05-19 14:50:19 +08:00

448 lines
17 KiB
C

/***********************************************************************************************************************
MMBasic
mouse.c
Handles the a few miscellaneous functions for the MX470 version.
Copyright 2016 - 2021 Peter Mather. All Rights Reserved.
This file and modified versions of this file are supplied to specific individuals or organisations under the following
provisions:
- This file, or any files that comprise the MMBasic source (modified or not), may not be distributed or copied to any other
person or organisation without written permission.
- Object files (.o and .hex files) generated using this file (modified or not) may not be distributed or copied to any other
person or organisation without written permission.
- This file is provided in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
************************************************************************************************************************/
#include "MMBasic_Includes.h"
#include "Hardware_Includes.h"
char *mouse0Interruptc=NULL;
volatile int mouse0foundc=0;
bool mouse0=false;
int mouseID=0;
volatile int readreturn=-1;
static volatile int PS2State, KCount, KParity, runmode=0;
bool mouseupdated=false;
volatile unsigned char Code = 0;
int MOUSE_CLOCK,MOUSE_DATA;
volatile short mouse[4];
volatile unsigned int bno=0;
volatile unsigned char LastCode = 0;
void setstream(void);
void mouse_init();
static bool sendCommand(int cmd);
int ReadReturn(int timeout);
// definition of the mouse PS/2 state machine
#define PS2START 0
#define PS2BIT 1
#define PS2PARITY 2
#define PS2STOP 3
#define PS2ERROR 9
#define MDATA 1
#define MCLK 0
#define HIGH 1
#define LOW 0
#define MouseTimeout 500
void MouseKBDIntEnable(int status){
if (status)
{
PinSetBit(MOUSE_CLOCK, TRISSET); // same for data
PinSetBit(MOUSE_DATA, TRISSET); // data low
if (!CallBackEnabled)
{
CallBackEnabled = 64;
gpio_set_irq_enabled_with_callback(PinDef[MOUSE_CLOCK].GPno, GPIO_IRQ_EDGE_FALL, true, &gpio_callback);
}
else
{
CallBackEnabled |= 64;
gpio_set_irq_enabled(PinDef[MOUSE_CLOCK].GPno, GPIO_IRQ_EDGE_FALL, true);
}
}
else
{
PinSetBit(MOUSE_CLOCK, TRISSET); // same for data
PinSetBit(MOUSE_DATA, TRISSET); // data low
if (CallBackEnabled == 64){
CallBackEnabled=0;
gpio_set_irq_enabled_with_callback(PinDef[MOUSE_CLOCK].GPno, GPIO_IRQ_EDGE_FALL, false, &gpio_callback);
} else {
gpio_set_irq_enabled(PinDef[MOUSE_CLOCK].GPno, GPIO_IRQ_EDGE_FALL, false);
CallBackEnabled &= (~64);
}
PS2State = PS2START;
}
}
void mousecheck(int n){
if(MouseTimer < MouseTimeout){
routinechecks();
return;
}
MouseKBDIntEnable(0); // disable interrupt in case called from within CNInterrupt()
runmode=0;
mouse0=false;
ExtCfg(MOUSE_CLOCK, EXT_NOT_CONFIG, 0);
ExtCfg(MOUSE_DATA, EXT_NOT_CONFIG, 0);
PS2State = PS2START;
error("Mouse timeout % ",n);
}
/***************************************************************************************************
initMouse
Initialise the mouse routine.
****************************************************************************************************/
void initMouse0(int sensitivity) {
if(!(MOUSE_CLOCK || Option.MOUSE_CLOCK))return;
if(!Option.MOUSE_CLOCK){
ExtCfg(MOUSE_CLOCK, EXT_COM_RESERVED, 0);
ExtCfg(MOUSE_DATA, EXT_COM_RESERVED, 0);
} else {
MOUSE_CLOCK=Option.MOUSE_CLOCK;
MOUSE_DATA=Option.MOUSE_DATA;
}
gpio_init(PinDef[MOUSE_CLOCK].GPno);
gpio_set_pulls(PinDef[MOUSE_CLOCK].GPno,true,false);
gpio_set_dir(PinDef[MOUSE_CLOCK].GPno, GPIO_IN);
gpio_set_input_hysteresis_enabled(PinDef[MOUSE_CLOCK].GPno,true);
gpio_init(PinDef[MOUSE_DATA].GPno);
gpio_set_pulls(PinDef[MOUSE_DATA].GPno,true,false);
gpio_set_dir(PinDef[MOUSE_DATA].GPno, GPIO_IN);
gpio_set_drive_strength(PinDef[MOUSE_DATA].GPno,GPIO_DRIVE_STRENGTH_8MA);
gpio_set_drive_strength(PinDef[MOUSE_CLOCK].GPno,GPIO_DRIVE_STRENGTH_8MA);
int maxW=HRes;
int maxH=VRes;
mouseID=0;
runmode=0;
MouseKBDIntEnable(0); // disable interrupt in case called from within CNInterrupt()
// enable pullups on the clock and data lines.
// This stops them from floating and generating random chars when no mouse is attached
// reserve the mouse pins
if(!sendCommand(0xFF))return; // Reset
ReadReturn(500);
sendCommand(0xF5); // Turn off streaming
ReadReturn(5);
if(sensitivity){
int scaling;
if(sensitivity>4){
scaling=1;
sensitivity-=4;
}
sensitivity--;
if(scaling){
sendCommand(0xE7); //
ReadReturn(5);
}
sendCommand(0xE8);
ReadReturn(5);
sendCommand(sensitivity);
ReadReturn(5);
}
sendCommand(0xF3); //
ReadReturn(5);
sendCommand(200); //
ReadReturn(5);
sendCommand(0xF3); //
ReadReturn(5);
sendCommand(100); //
ReadReturn(5);
sendCommand(0xF3); //
ReadReturn(5);
sendCommand(80); //
ReadReturn(5);
sendCommand(0xF2); //
mouseID=ReadReturn(10);
sendCommand(0xF3); //
ReadReturn(5);
sendCommand(200); //
ReadReturn(5);
sendCommand(0xF4); // Turn on streaming
ReadReturn(5);
// setup Change Notification interrupt
PS2State = PS2START;
memset((struct s_nunstruct *)&nunstruct[2],0,sizeof(struct s_nunstruct));
nunstruct[2].classic[0]=mouseID;
nunstruct[2].type=0; //used for the double click timer
nunstruct[2].ax=maxW/2;
nunstruct[2].ay=maxH/2;
runmode=1;
Code = 0;
bno=0;
LastCode = 0;
// __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_3);
MouseKBDIntEnable(1); // enable interrupt
mouse0=true;
}
void mouse0close(void){
if(!mouse0)return;
mouseID=0;
runmode=0;
sendCommand(0xFF); // Turn off streaming
ReadReturn(5);
MouseKBDIntEnable(0); // disable interrupt in case called from within CNInterrupt()
ExtCfg(MOUSE_CLOCK, EXT_NOT_CONFIG, 0);
ExtCfg(MOUSE_DATA, EXT_NOT_CONFIG, 0);
mouse0=false;
runmode=0;
mouse0Interruptc=NULL;
memset((struct s_nunstruct *)&nunstruct[2],0,sizeof(struct s_nunstruct));
}
static bool sendCommand(int cmd)
{
int i, j;
// calculate the parity and add to the command as the 9th bit
for (j = i = 0; i < 8; i++)
j += ((cmd >> i) & 1);
cmd = (cmd & 0xff) | (((j + 1) & 1) << 8);
PinSetBit(MOUSE_CLOCK, TRISCLR);
PinSetBit(MOUSE_CLOCK, LATCLR);
uSec(250);
PinSetBit(MOUSE_DATA, TRISCLR);
PinSetBit(MOUSE_DATA, LATCLR);
PinSetBit(MOUSE_CLOCK, TRISSET);
InkeyTimer = 0;
uSec(2);
while (PinRead(MOUSE_CLOCK))
if (InkeyTimer >= 500)
{ // wait for the keyboard to pull the clock low
return false; // wait for the keyboard to pull the clock low
}
// send each bit including parity
for (i = 0; i < 9; i++)
{
if (cmd & 1)
{
PinSetBit(MOUSE_DATA, LATSET);
}
else
{
PinSetBit(MOUSE_DATA, LATCLR);
}
while (!PinRead(MOUSE_CLOCK))
if (InkeyTimer >= 500)
{ // wait for the keyboard to pull the clock low
return false; // wait for the keyboard to pull the clock low
}
while (PinRead(MOUSE_CLOCK))
if (InkeyTimer >= 500)
{ // wait for the keyboard to pull the clock low
return false; // wait for the keyboard to pull the clock low
}
cmd >>= 1;
}
// PinSetBit(MOUSE_CLOCK, TRISSET);
PinSetBit(MOUSE_DATA, TRISSET);
while (PinRead(MOUSE_DATA))
if (InkeyTimer >= 500)
{ // wait for the keyboard to pull the clock low
return false; // wait for the keyboard to pull the clock low
} // wait for the keyboard to pull data low (ACK)
while (PinRead(MOUSE_CLOCK))
if (InkeyTimer >= 500)
{ // wait for the keyboard to pull the clock low
return false; // wait for the keyboard to pull the clock low
} // wait for the clock to go low
while (!(PinRead(MOUSE_CLOCK)) || !(PinRead(MOUSE_DATA)))
if (InkeyTimer >= 500)
{ // wait for the keyboard to pull the clock low
return false; // wait for the keyboard to pull the clock low
}
return true;
}
/***************************************************************************************************
sendCommand - Send a command to to mouse.
****************************************************************************************************/
int ReadReturn(int timeout){
int i;
Code = 0;
bno=0;
LastCode = 0;
readreturn=-1;
MouseKBDIntEnable(1);
i=100;
MouseTimer = 0;
while(MouseTimer<timeout){
if(readreturn!=-1){
i=readreturn;
readreturn=-1;
routinechecks();
}
}
return i;
}
/***************************************************************************************************
change notification interrupt service routine
****************************************************************************************************/
void __not_in_flash_func(MNInterrupt)(uint64_t dd) {
static unsigned long long int lefttimer=0, righttimer=0;
int maxW=HRes;
int maxH=VRes;
int d = dd & (1<<PinDef[MOUSE_DATA].GPno);
// Make sure it was a falling edge
if (!(dd & (1<<PinDef[MOUSE_CLOCK].GPno)))
{
if(!Timer5)PS2State=PS2START;
// Sample the data
switch(PS2State){
default:
case PS2ERROR: // this can happen if a timing or parity error occurs
// fall through to PS2START
case PS2START:
if(!d) { // PS2DAT == 0
KCount = 8; // init bit counter
KParity = 0; // init parity check
Code = 0;
PS2State = PS2BIT;
Timer5=5;
}
break;
case PS2BIT:
Code >>= 1; // shift in data bit
if(d) Code |= 0x80; // PS2DAT == 1
KParity ^= Code; // calculate parity
if (--KCount <= 0) PS2State = PS2PARITY; // all bit read
break;
case PS2PARITY:
if(d) KParity ^= 0x80; // PS2DAT == 1
if (KParity & 0x80) { // parity odd, continue
PS2State = PS2STOP;
} else {
PS2State = PS2ERROR;
}
break;
case PS2STOP:
if(d) { // PS2DAT == 1
readreturn=Code;
if(runmode){
mouse[bno++]=Code;
if(!(mouse[0] & 0x08))bno=0;//bit 3 must be set in first byte
if(bno==(mouseID==3 ? 4: 3)){
bno=0;
if(mouse[0] & 0b10000)mouse[1] |=0xFF00;
if(mouse[0] & 0b100000)mouse[2] |=0xFF00;
nunstruct[2].ax+=mouse[1];
if(nunstruct[2].ax<0)nunstruct[2].ax=0;
if(nunstruct[2].ax>=maxW)nunstruct[2].ax=maxW-1;
nunstruct[2].ay-=mouse[2];
mouseupdated=1;
if(nunstruct[2].ay<0)nunstruct[2].ay=0;
if(nunstruct[2].ay>=maxH)nunstruct[2].ay=maxH-1;
nunstruct[2].L = mouse[0] & 0b1;
nunstruct[2].R=(mouse[0] & 0b10)>>1;
nunstruct[2].C=(mouse[0] & 0b100)>>2;
if(nunstruct[2].type>1000) nunstruct[2].Z=0;
if((mouse[0] & 3) != (LastCode & 3)){
if((mouse[0] & 1) && !(LastCode & 1) && (mSecTimer-lefttimer>16)){ //left button press
nunfoundc[2]=1;
if(nunstruct[2].type>=500 || nunstruct[2].type<100) nunstruct[2].type=0;
else {
nunstruct[2].Z=1;
nunstruct[2].type=500 ;
}
lefttimer=mSecTimer;
}
if(!(mouse[0] & 1) && (LastCode & 1)){ //left button release
}
if((mouse[0] & 2) && !(LastCode & 2) && (mSecTimer-righttimer>16)){
righttimer=mSecTimer;
}
}
LastCode=mouse[0];
if(mouseID==3){
if(mouse[3] & 0x80)mouse[3]|=0xFF00;
nunstruct[2].az=(volatile int)nunstruct[2].az+mouse[3];
}
nunstruct[2].x1=nunstruct[2].ax/(FontTable[gui_font >> 4][0] * (gui_font & 0b1111));
nunstruct[2].y1=nunstruct[2].ay/(FontTable[gui_font >> 4][1] * (gui_font & 0b1111));
}
}
}
PS2State = PS2START;
break;
}
}
}
void cmd_mouse(void){
unsigned char *tp=NULL;
int n;
if((tp=checkstring(cmdline, (unsigned char *)"OPEN"))){
getargs(&tp,7,(unsigned char *)",");
// if(Option.MOUSE_CLOCK)error("Already open");
if(!(argc==5))error("Syntax");
getint(argv[0],2,2);
char code;
if(!(code=codecheck(argv[2])))argv[2]+=2;
int pin1 = getinteger(argv[2]);
if(!code)pin1=codemap(pin1);
if(!(code=codecheck(argv[4])))argv[4]+=2;
int pin2 = getinteger(argv[4]);
if(!code)pin2=codemap(pin2);
if(IsInvalidPin(pin1)) error("Invalid pin %/|",pin1,pin1);
if(IsInvalidPin(pin2)) error("Invalid pin %/|",pin2,pin2);
if(!(pin1==Option.MOUSE_CLOCK && pin2==Option.MOUSE_DATA)){
if(ExtCurrentConfig[pin1] >= EXT_COM_RESERVED ) error("Pin %/| is in use",pin1,pin1);
if(ExtCurrentConfig[pin2] >= EXT_COM_RESERVED ) error("Pin %/| is in use",pin2,pin2);
}
if(Option.MOUSE_CLOCK && !(pin1==Option.MOUSE_CLOCK && pin2==Option.MOUSE_DATA))error("OPTION MOUSE declared with different pins");
MOUSE_CLOCK=pin1;
MOUSE_DATA=pin2;
if(!mouse0)initMouse0(0);
if(!mouse0){
if(!Option.MOUSE_CLOCK){
ExtCfg(MOUSE_CLOCK, EXT_NOT_CONFIG, 0);
ExtCfg(MOUSE_DATA, EXT_NOT_CONFIG, 0);
}
MOUSE_CLOCK=0;
MOUSE_DATA=0;
error("Open failed");
}
} else if((tp=checkstring(cmdline, (unsigned char *)"CLOSE"))){
getargs(&tp,1,(unsigned char *)",");
if(!mouse0)error("Not open");
if(Option.MOUSE_CLOCK)error("Option MOUSE set - close invalid");
n=getint(argv[0],2,2);
mouse0close();
} else if((tp=checkstring(cmdline,(unsigned char *)"INTERRUPT ENABLE"))){
getargs(&tp,3,(unsigned char *)",");
if(!mouse0)error("Not open");
if(!(argc==3))error("Syntax");
n=getint(argv[0],2,2);
nunInterruptc[n] = (char *)GetIntAddress(argv[2]); // get the interrupt location
InterruptUsed = true;
return;
} else if((tp = checkstring(cmdline, (unsigned char *)"SET"))){
getargs(&tp,7,(unsigned char *)",");
if(!mouse0)error("Not open");
if(!(argc==7))error("Syntax");
n=getint(argv[0],2,2);
nunstruct[n].ax=getint(argv[2],-HRes,HRes);
nunstruct[n].ay=getint(argv[4],-VRes,VRes);
nunstruct[n].az=getint(argv[6],-1000000,1000000);
} else if((tp = checkstring(cmdline, (unsigned char *)"INTERRUPT DISABLE"))){
getargs(&tp,1,(unsigned char *)",");
if(!mouse0)error("Not open");
n=getint(argv[0],2,2);
nunInterruptc[n]=NULL;
} else error("Syntax");
}