From 51151d9396cc3a84341ce718c8a121079d9a5487 Mon Sep 17 00:00:00 2001 From: Michel Gallant Date: Mon, 7 Jan 2013 23:47:03 -0500 Subject: [PATCH] Initial checkin of PS2 to ADB converter --- ps2adb.ino | 476 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 476 insertions(+) create mode 100644 ps2adb.ino diff --git a/ps2adb.ino b/ps2adb.ino new file mode 100644 index 0000000..9526256 --- /dev/null +++ b/ps2adb.ino @@ -0,0 +1,476 @@ +/* + * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* Copyright (c) 1991 NeXT Computer, Inc. All rights reserved. + * Copyright (c) 2013 Michel Gallant. All rights reserved. + * + * adb_bus.h - Architecture-indpendent ADB bus defintions. + * + * HISTORY + * 5-Jan-2013 Michel Gallant at Foulab + * Created. + */ + +#include +#define PS2DATA 4 +#define PS2CLOCK 3 +#define ADB 1 + +//State machine states +#define INIT 1 +#define ATTENTION 2 +#define SYNC 3 +#define GOTSTOPBIT 4 + +// Input compare interrupt on +#define ENABLE_CAP_INT TIMSK1 |= 0x20; +#define DISABLE_CAP_INT TIMSK1 &= ~0x20; + +//ADB commands +#define TALK 3 +#define LISTEN 2 + +// PS2 Mouse button butmasks +#define LBUTTON 1 +#define RBUTTON 2 + +//Raw logic level of input. +volatile byte level; + +//Time since last level change +volatile unsigned int time; + +// By default, a mouse has address 3. +// This can be changed by the computer. +// It is stored in register 3. +#define INIT_DEVICE_ADDRESS 3 +#define INIT_HANDLER_ID 1 + +// Data registers. Register 0 is mouse movement data. +// Register 3 is device address and status bits. +// Registers 1 and 2 are unused on most devices. +volatile byte reg0[2]; +//byte reg3[2]; + +volatile byte state = INIT; + +//TODO: Why does this seem backwards from the cmdBits struct below? +typedef struct { + + byte reserved0:1, // bit 15, always 0 + exceptionalEvent:1, // bit 14 + serviceRequestEnable:1, // bit 13 + reserved1:1, // bit 12, always 0 + deviceAddress:4; // bits 11..8 + byte handlerId:8; // bits 7..0 + +} adbRegister3bits; + +typedef struct { + byte b[2]; +} adbRegister3byte; + +typedef union { + adbRegister3bits bits; + adbRegister3byte bytes; +} adbRegister3; + +typedef struct{ + byte reg:2, // Bits 0..1 + cmd:2, // Bits 2..3 + address:4; // Bits 4..7 +}cmdBits; + +typedef struct{ + byte cmdByte; +}cmdByte; + +typedef union{ + cmdBits bits; + cmdByte bytes; +}cmd; + +//typedef struct{ + + +adbRegister3 reg3; + +#define TLEN 256 +//volatile unsigned int t[TLEN]; +//volatile unsigned int l[TLEN]; +//volatile unsigned int a[TLEN]; +//volatile unsigned int* volatile times; +//volatile unsigned int* endtimes; +//volatile unsigned int* volatile levels; +//volatile unsigned int* endlevels; + +volatile int working; + +volatile cmd command; + +volatile unsigned int syncs = 0; +volatile unsigned int attens = 0; +volatile unsigned long int acalls = 0; +volatile unsigned long int aloops = 0; + +volatile byte pending = 0; + +PS2Mouse mouse( PS2CLOCK, PS2DATA ); + + +void setup(){ + /*times = t; + endtimes = times + TLEN; + levels = l; + endlevels = levels + TLEN;*/ + Serial.begin(115200); + mouse.initialize(); + reg3.bytes.b[0] = 0; + reg3.bytes.b[1] = 0; + reg3.bits.deviceAddress = INIT_DEVICE_ADDRESS; + reg3.bits.handlerId = INIT_HANDLER_ID; + Serial.print("Starting ADB. Device address: "); + Serial.print( reg3.bits.deviceAddress ); + Serial.print(" Handler ID: " ); + Serial.println( reg3.bits.handlerId ); + // Set data direction in + DDRB &= ~ADB; + // Enable weak pullup + PORTB |= ADB; + // Set up timer-counter #1 + TCCR1A = 0x00; + // Input compare noise canceller on, + // Falling edge input compare interrupt, + // clock prescaler = 8 + TCCR1B = 0x82; + TCCR1C = 0x00; + TCNT1= 0x00; + // Input compare interrupt on + ENABLE_CAP_INT + + // If ADB input is high, set falling-edge interrupt. + // Otherwise, set rising-edge input + //(level = PORTB & ADB);//? TCCR1B &= 0xBF: TCCR1B |= 0x40; + TCCR1B |= 0x40; + working = 1; +} + +void loop(){ + //int data[3]; + //printMouseReport(data); + //delay(200); + //Serial.println( "Waiting for HIGH" ); + if( !working ){ + Serial.println( "DONE WORKING"); + /*for( int i =0; i < TLEN; i++){ + Serial.print( " Level : " ); + Serial.print( l[i] ? "H" : "L" ); + Serial.print( " Time: " ); + Serial.println( t[i] ); + + }*/ + Serial.print( "Syncs, attentions, acalls, aloops:: " ); + Serial.print( syncs ); + Serial.print(", "); + Serial.print( attens ); + Serial.print(", "); + Serial.print( acalls ); + Serial.print(", "); + Serial.println( aloops ); + while(1); +} + + getHigh(); + getAttentionSignal(); + //Serial.println(time); + if( state != ATTENTION ) return; + getSyncSignal(); + if( state != SYNC ) return; + //Serial.println( "Got sync!" ); + // Get command byte + + for( int i = 0; i < 8; i++ ){ + byte b = getBit(); + if ( b > 1 ) return; + command.bytes.cmdByte = ( command.bytes.cmdByte << 1 ) + b; + } + + /*command.bits.reg = 2; + Serial.println( command.bits.address ); + Serial.println( command.bits.cmd ); + Serial.println( command.bits.reg ); + Serial.println( command.bytes.cmdByte, HEX ); + Serial.println( reg3.bits.deviceAddress ); + Serial.print( reg3.bytes.b[0], HEX ); + Serial.println( reg3.bytes.b[1], HEX );*/ + if( command.bits.address == reg3.bits.deviceAddress ){ + //Serial.println("ME!"); + //pending = 1; + if( command.bits.cmd == TALK && command.bits.reg == 0 && pending ){ + getStopAndTlt(); + //Serial.print( "Got Tlt. time = " ); + //Serial.println( time ); + sendMouse(); + } + + //TODO: Handle LISTEN and FLUSH here + + } + + //TODO: Handle global commands here + + // Check for new movement/ buttons from the mouse. + // If there is data pending, don't check, because that would + // overwrite the currently pending data. The mouse + // should accumulate any extra movement that happens + // during this time. + if( !pending ) checkMouse(); + + /*Serial.print( "cb " ); + Serial.println( command.bytes.cmdByte, HEX );*/ + + + + +} + +void printMouseReport(int data[]){ + mouse.report(data); + Serial.print( "RAW: "); + Serial.print( data[0], HEX ); + Serial.print(" Status: X - "); + Serial.print( data[0]& 1<<4 ? data[1] : -data[1] ); + Serial.print(" Y - "); + Serial.print( data[0] & 1<<5 ? data[2] : -data[2] ); + Serial.print(" L - "); + Serial.print( (data[0] & 1<<0) ? "D" : "U" ); + Serial.print(" R - "); + Serial.println( data[0] & 1<<1 ? "D" : "U" ); +} + +void getHigh(){ + + while( !level ); + return; + + //asm("cli"); +} + +void getAttentionSignal(){ + while( !level ){ + aloops++; + }; + acalls++; + + //TODO: Attention time on the IIgs seems longer. + //Increase to 827 or more... maybe 850? + if( time > 776 && time < 850 ){ + state = ATTENTION; + attens++; + } +} + +void getSyncSignal(){ + while( level ); + if( time > 67 && time < 80 ){ + state = SYNC; + syncs++; + } +} + +byte getBit(){ + // b is the candidate - what we suspect the bit will be. + // If both the high and low times match the protocol spec, + // return b, otherwise return error. + byte b; + while( !level ); + if( time < 50 ){ + b = 1; + }else if( time >= 50 && time < 72 ){ + b = 0; + }else{ + return 2; + } + + while( level ); + if( b == 1 && time >= 50 && time < 72 ){ + return 1; + }else if( b == 0 && time < 50 ){ + return 0; + }else{ + return 2; + } + +} + +ISR(TIMER1_CAPT_vect){ + // Disable interrupts + asm("cli"); + // Clear counter register + TCNT1 = 0; + // Save line level to level value + //level = PORTB & ADB; + // Save match register to time value + // The timer ticks twice per microsecond, + // so dividing by two gives us actual microseconds + time = ICR1 >> 1; + // Set next interrupt to happen on appropriate + // rising or falling edge + //level ? TCCR1B &= 0xBF: TCCR1B |= 0x40; + level = (TCCR1B & 0x40); + TCCR1B ^= 0x40; + //*(times++) = time; + //*(levels++) = !level; + /*if( levels == endlevels ){ + working = 0; + TIMSK1&= ~0x20; + //times = t; + //levels = l; + }*/ + // Enable interrupts + asm("sei"); + //Serial.println( time ); + + +} + +void getStopAndTlt(){ + + while( !level ); + //If we have a 0, this is the stop bit + if( time > 50 && time < 65 ){ + state = GOTSTOPBIT; + // If time is around 300 uS low, then some other device + // has requested service and we can't transmit + }else if( time > 280 && time < 330 ){ + state = INIT; + }else{ + //TODO: probably should be COLLISION not INIT + state = INIT; + } + + // Delay until Tlt is reached + // 200mS, which is 400 counter counts + while( level && TCNT1 < 400 ); + +} + +void sendMouse(){ + // Disable timer interrupts + DISABLE_CAP_INT + //start bit + sendBit( 1 ); + sendByte( reg0[0] ); + sendByte( reg0[1] ); + //sendByte( /*reg0[0] */ 0x00 ); + //sendByte( /*reg0[1]*/ 0x80 ); + //stop bit + sendBit( 0 ); + pending = 0; + //Serial.println("M"); + //re-enable timer interrupt + ENABLE_CAP_INT +} + + +//TODO: ADD COLLISION DETECTION HERE! +inline void sendBit( byte b ){ + if(b){ + goLow(); + delayMicroseconds(33); + goHigh(); + delayMicroseconds(61); + }else{ + goLow(); + delayMicroseconds(61); + goHigh(); + delayMicroseconds(33); + } +} + +void sendByte( byte b ){ + // Send a byte MSB first. + for( int i = 0; i < 8; i ++ ){ + sendBit( b & 0x80 ); + b = b << 1; + } +} + +inline void goLow(){ + //Turn off pullup + PORTB &= ~ADB; + // Set direction output + DDRB |= ADB; +} + +inline void goHigh(){ + // Set direction input + DDRB &= ~ADB; + //Turn on pullup + PORTB |= ADB; +} + +// Check for new movement/ buttons from the mouse. +// Transfer to to register 0 and set the +// pending flag if there is new data. +void checkMouse(){ + static byte oldbuttons = 0; + static byte buttons = 0; + int data[3]; + + // Ignore the ADB bus while we read the PS2 mouse. + // We don't care what's happening there. + DISABLE_CAP_INT + //2 mS + mouse.report(data); + // Looks like the mouse library already sign-extends x and y values + // Now we just downconvert them to 7 bit values, + // capping them if they would otherwise overflow 7 bits + if( data [1] < -64 ) data[1]=-64; + else if( data[1] > 63 ) data[1] = 63; + // Vertical axis seems reversed from PS2 mouse + data[2] = -data[2]; + if( data [2] < -64 ) data[2]=-64; + else if( data[2] > 63 ) data[2] = 63; + // Move Y value into reg 0 byte 1 + reg0[0] = data[2] & 0x7F; + // Move x value into reg 0 byte 2 + reg0[1] = data[1] & 0x7F; + // TODO: check for button changes here + buttons = data[0] & 0x03; + if( reg0[0] || reg0[1] || buttons != oldbuttons ){ + pending = 1; + Serial.print( "Got mouse event. x: "); + Serial.print( data[1] ); + Serial.print( ", y: "); + Serial.print( data[2] ); + Serial.print( ", btns: " ); + Serial.println( buttons ); + } + if ( !(buttons & LBUTTON) ) reg0[0] |= 0x80; + if ( !(buttons & RBUTTON) ) reg0[1] |= 0x80; + + oldbuttons = buttons; + ENABLE_CAP_INT +}