/******************************************************************************* *PS2.C Robert Buels, rmb32@cornell.edu *Polls the AT90S1200 chip and translates controller bits to ps/2 packets *April, 2002 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed 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. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * ******************************************************************************/ #include <90s8515.h> #include //#include #asm .equ PORTA=0x1b .equ PORTB=0x18 .equ PINA=0x19 .equ DDRA=0x1a .def temp=r17 .def delay=r18 .def output=r19 .def input=r19 .def temp2=r20 .def parity=r21 .def ptemp=r22 .equ CLOCK=PORTA,1 .MACRO __SETDATA cbi DDRA,0 ;DDRA.0 = 0 sbi PORTA,0 ;PORTA.0 = 1 .ENDM .MACRO __CLEARDATA sbi DDRA,0 ;DDRA.0 = 1 cbi PORTA,0 ;PORTA.0 = 0 .ENDM .MACRO __SETCLOCK cbi DDRA,1 sbi PORTA,1 .ENDM .MACRO __CLEARCLOCK sbi DDRA,1 cbi PORTA,1 .ENDM #endasm #pragma regalloc- #define TIMER0RELOAD 62 #define DPAD_SPEED 3 #define ANALOG_THRESHOLD 9 unsigned char buttonState,lastbuttonState; char deltaX, deltaY, deltaZ,extendedByte; char lastdeltaX, lastdeltaY, lastdeltaZ,lastextendedByte; char packet1,packet2,packet3,packet4,extended; char transmitted; unsigned char hostCommand,mouseResponse; char error; char transmitMode; char settingSampleRateFlag; char setResolutionFlag; char sampleRate,lastSampleRate,lastlastSampleRate; char resolution; int timeTillSample; unsigned char deviceID; #pragma regalloc+ void receiveByte(); void sendData(); void sendByte(); void processCommand(); void readN64(); char notEqual(char,char); void init() { PORTA = 0x03; DDRA = 0x00; //port A: PS2 in-out DDRB = 0x03; //port B: control to 1200 PORTB = 0x00; //portb pullups not activated DDRC = 0x00; //portc hi-z PORTC = 0x00; DDRD = 0x00; //port D: input from 1200 PORTD = 0x00; //make sure the pullup resistors aren't activated: hi-z transmitted = 1; mouseResponse = 0xFA; setResolutionFlag = 0; error = 0; resolution = 4; sampleRate = lastSampleRate = lastlastSampleRate = 100; settingSampleRateFlag = 0; deltaZ = deltaY = deltaX = 0; packet1 = packet2 = packet3 = packet4 = extended = 0; timeTillSample = 1000; //initial sample rate 100Hz deviceID = 0x00; //standard PS/2 mouse TCNT0 = TIMER0RELOAD; TIMSK = 2; //turn on timer 0 overflow ISR TCCR0 = 3; //prescalar to 64 //For 8515: /* UCR = 0x18; UBRR = 25; //using a 4 MHz crystal putsf("reset\r\n"); */ transmitMode = 0; //initially, not in data streaming mode delay_ms(400); mouseResponse = 0xAA; sendByte(); mouseResponse = 0x00; sendByte(); #asm("sei") } //timer 0 overflow ISR interrupt [TIM0_OVF] void timer0_overflow() { //reload for 1 mSec overflow TCNT0=TIMER0RELOAD; //Decrement time if not already 0 if (timeTillSample>0) timeTillSample--; } void main() { init(); readN64(); readN64(); while(1) { /* //readN64(); mouseResponse = ~PIND; sendByte(); // sendData(); delay_us(800); PORTB++; */ if(!PINA.0) { receiveByte(); if(!error) processCommand(); else { //handle error //printf("Error: %d\r\n",error); switch (error) { case 0x01: break; //aborted, do nothing case 0x02: break; //data early release, do nothing case 0x03: //parity error, ask for a resend mouseResponse = 0xFE; sendByte(); break; case 0x04: //data not released, send an error mouseResponse = 0xFC; sendByte(); break; } } } if(!timeTillSample) readN64(); if(transmitMode && ((lastbuttonState != buttonState) || (deltaX) || (deltaY) || (deltaZ))) { transmitted = 1; sendData(); } } } void processCommand() { //printf("h:0x%x(%d)\r\n ",hostCommand,hostCommand); delay_us(100); //acknowledge #asm ldi output,0xFA rcall __ByteOut #endasm if(setResolutionFlag) { //putsf("\r\n"); setResolutionFlag = 0; if(hostCommand >= 0 && hostCommand <= 3) resolution = hostCommand; if(hostCommand < 0 || hostCommand > 3) { //send an error // putsf("\r\nBadrez\r\n"); #asm ldi output,0xFC rcall __ByteOut #endasm return; } else { resolution = hostCommand; // printf("R=%d\r\n",resolution); return; } return; } if(settingSampleRateFlag) { settingSampleRateFlag = 0; if(hostCommand >= 10 && hostCommand <= 200) { lastlastSampleRate = lastSampleRate; lastSampleRate = sampleRate; sampleRate = hostCommand; } if(hostCommand >= 10 && hostCommand <= 200) { // printf("sr=>%d\r\n",hostCommand,hostCommand); lastlastSampleRate = lastSampleRate; lastSampleRate = sampleRate; sampleRate = hostCommand; if((lastlastSampleRate == 200) && (lastSampleRate == 100) && (sampleRate == 80)) { // putsf("MS3\r\n"); deviceID = 0x03; // enter MS intellimouse mode } if(deviceID == 0x03 && lastlastSampleRate == 200 && lastSampleRate == 200 && sampleRate == 80) { // putsf("MS5\r\n"); deviceID = 0x04; // enter MS 5-button mouse mode } } else { // bad sample rate, output an error // putsf("\r\nBadrate\r\n"); #asm ldi output,0xFC rcall __ByteOut #endasm } return; } switch (hostCommand) { case 0xFF: //reset 0xAA, 0x00 //putsf(" Rset\r\n"); delay_ms(400); #asm ldi output,0xAA rcall __ByteOut lds output,_deviceID rcall __ByteOut #endasm transmitMode = 0; sampleRate = 100; deviceID = 0x00; break; case 0xF4: //enable transmit //putsf("rp +\r\n"); transmitMode = 1; break; // case 0xE6: //set scaling 1:1 // case 0xE7: //set scaling 2:1 case 0xF2: //get device ID //printf("ID=0x%x\r\n",deviceID); delay_ms(1); #asm lds output,_deviceID rcall __ByteOut #endasm break; /* case 0xEB: //read data, for remote mode if(remoteMode) sendData(); break;*/ case 0xE8: //set resolution //putsf(" Setres\r\n"); setResolutionFlag = 1; break; case 0xE9: //status request respond 0xFA, 0x00, 0x02, 0x64 //putsf(" Sreq\r\n"); #asm lds output, _transmitMode swap output lsr output andi output,0x20 ; set the data reporting bit of the first status byte rcall __ByteOut lds output, _resolution rcall __ByteOut lds output, _sampleRate rcall __ByteOut #endasm break; // case 0xEA: //set stream mode // case 0xEB: //read data (for Remote Mode) // case 0xEC: //reset wrap mode // case 0xEE: //set wrap mode // case 0xF0: //set remote mode case 0xF3: //set sample rate //putsf(" set srate\r\n"); settingSampleRateFlag = 1; break; case 0xF5: //disable data reporting //putsf(" rep-\r\n"); transmitMode = 0; break; case 0xF6: //set defaults //putsf(" dflts\r\n"); transmitMode = 0; sampleRate = 100; resolution = 4; break; default: //all other commands //putsf(" ?\r\n"); } } // uses analog stick to move the mouse void readN64() { char temp; timeTillSample = 1000/sampleRate; transmitted = 0; PORTB = 0; delay_us(200); packet1 = PIND; PORTB = 1; delay_us(200); //we're not using these packet2 = PIND; // PORTB = 2; // delay_us(200); // packet3 = PIND; PORTB = 3; delay_us(200); packet4 = PIND; lastbuttonState = buttonState; buttonState = 0x08; lastdeltaY = deltaY; deltaY = 0; lastdeltaX = deltaX; deltaX = 0; if(packet2 > 127) { //it's negative if(packet2 < (0xFF-ANALOG_THRESHOLD)) { deltaX = ((packet2+ANALOG_THRESHOLD)>>2) | 0b11000000; buttonState |= 0x10; } } else if(packet2 > ANALOG_THRESHOLD) { //it's positive deltaX = (packet2-ANALOG_THRESHOLD)>>2; } if(packet4 > 127) { //it's negative if(packet4 < (0xFF-ANALOG_THRESHOLD)) { deltaY = ((packet4+ANALOG_THRESHOLD)>>2) | 0b11000000; buttonState |= 0x20; } } else if(packet4 > ANALOG_THRESHOLD) { //it's positive deltaY = (packet4-ANALOG_THRESHOLD)>>2; } //translate the stuff into PS2 packets buttonState |= packet1 >> 5; //now is 00YX1ABZ } /*// WORKS: uses DPAD to move mouse void readN64() { timeTillSample = 1000/sampleRate; transmitted = 0; PORTB = 0; delay_us(200); packet1 = PIND; lastbuttonState = buttonState; buttonState = 0x08; lastdeltaY = deltaY; deltaY = 0; lastdeltaX = deltaX; deltaX = 0; if(packet1 & 0x08) { //dpad up deltaY = DPAD_SPEED; }else if(packet1 & 0x04) { //dpad down deltaY = -DPAD_SPEED; buttonState |= 0x20; } if(packet1 & 0x02) { //dpad left deltaX = -DPAD_SPEED; buttonState |= 0x10; } else if(packet1 & 0x01) { //dpad right deltaX = DPAD_SPEED; } //translate the stuff into PS2 packets buttonState |= packet1 >> 5; //now is 00YX1ABZ } */ char notEqual(char new, char old) { char s; s = new - old; if(s > -4 && s < 4) return 0; return 1; } void sendByte() { #asm lds output,_mouseResponse rcall __ByteOut #endasm } void receiveByte() { #asm __SETDATA __SETCLOCK ; make sure i am not holding down data or clock ByteIn: sbis PINA,1 ; wait for clock high rjmp ByteIn __DELAY_USB 100 sbic PINA,0 ; is data still low? if not, host aborted rjmp DataEarlyRelease __DELAY_USB 80 ldi temp2,8 ByteInLoop: rcall __BitIn sbis PINA,1 ; check inhibit rjmp Aborted dec temp2 brne ByteInLoop ;;read parity bit mov input,temp clr temp rcall __BitIn mov parity,temp rol parity rol parity sbis PINA,1 ; check inhibit rjmp Aborted ;;read stop bit rcall __BitIn sbis PINA,1 ; check inhibit rjmp Aborted __DELAY_USB 100 ;;wait for the data line to come high sbis PINA,0 ; if not high by now, generate an error rjmp WaitForDataHigh ;;output the acknowledge bit __DELAY_USB 20 ; delay about 15 us __CLEARDATA __DELAY_USB 9 ; delay about 5 ms __CLEARCLOCK __DELAY_USB 54 ; 40 ms __SETCLOCK __DELAY_USB 9 __SETDATA mov temp,input mov temp2,input swap temp2 eor temp,temp2 mov temp2,temp lsr temp lsr temp eor temp,temp2 mov temp2,temp lsr temp eor temp2,temp com temp2 ;now lsb of temp2 is odd parity ;; now lsb of temp2 is the parity of what the host sent andi temp2,0x01 cp temp2,parity brne ParityWrong __DELAY_USB 55 ;delay about 45 us rjmp NormalExit WaitForDataHigh: ldi delay,0x04 ; remember to send an error code in the main loop rcall __BitIn sbrs temp,0 rjmp WaitForDataHigh rjmp Aborted ParityWrong: ldi delay,0x03 ; remember to ask for a resend in main loop rjmp ByteInEnd DataEarlyRelease: ldi delay,0x02 rjmp ByteInEnd Aborted: ldi delay,0x01 rjmp ByteInEnd __BitIn: __DELAY_USB 27 __CLEARCLOCK __DELAY_USB 54 ; delay 40 us __SETCLOCK __DELAY_USB 27 ; 20 us lsr temp ;read a bit from the port sbic PINA,0 ori temp,0x80 ret NormalExit: ldi delay,0x00 ;; out PORTB,delay ByteInEnd: sts _error,delay sts _hostCommand,input #endasm } void sendData() { #asm lds output,_buttonState rcall __ByteOut lds output,_deltaX rcall __ByteOut lds output,_deltaY rcall __ByteOut #endasm if(deviceID == 0x03) { #asm lds output,_deltaZ rcall __ByteOut #endasm } else if(deviceID == 0x04) { #asm lds output,_extendedByte rcall __ByteOut #endasm } #asm __SETDATA ;let the data and clock lines float __SETCLOCK #endasm } //ByteOut: an assembly procedure for outputting the byte in register output to the PS2 port #asm .MACRO __CHECKINHIBIT sbis PINA,1 rjmp ByteOutDone .ENDM __ByteOut: clr temp sts _error,temp Inhibited: sbis PINA,1 ; check if clock is being held low, wait here till its high rjmp Inhibited __DELAY_USB 60 ; delay about 50 us, taking parity comp into account ;; while we are waiting, why not just calculate parity? mov temp,output mov parity,output swap parity eor temp,parity mov parity,temp lsr temp lsr temp eor temp,parity mov parity,temp lsr temp eor parity,temp ; now lsb of parity is even parity com parity ; invert it to get odd parity sbis PINA,1 ; check again for inhibited rjmp Inhibited ;; by now, we can definitely send sbis PINA,0 ; if data is low, host wants to send. Abort rjmp ByteOutAborted __DELAY_USB 27 ; delay 20 us ldi temp,0 rcall __OutBit ; output a 0 (the start bit) __CHECKINHIBIT mov temp,output rcall __OutBit ldi temp2,7 ByteOutLoop: asr temp rcall __OutBit __CHECKINHIBIT dec temp2 brne ByteOutLoop __DELAY_USB 3 mov temp,parity rcall __OutBit ; output the parity bit __DELAY_USB 8 ser temp rcall __OutBit ; output the stop bit (1) __DELAY_USB 33 ldi delay,0x00 sts _error,delay rjmp ByteOutDone ByteOutAborted: ldi delay,0x01 sts _error,delay ByteOutDone: __SETCLOCK __SETDATA ret ;;outputs a single bit to the ps2 line __OutBit: ; function to output the bit in temp to the ps2 data line sbrs temp,0 rjmp OutBitClear rjmp OutBitSet OutBitClear: __CLEARDATA rjmp OutBit2 OutBitSet: __SETDATA OutBit2: __DELAY_USB 30 ; delay a little less than 20 us __CLEARCLOCK __DELAY_USB 54 ; 40 us __SETCLOCK __DELAY_USB 25 ret #endasm