Initial checkin of PS2 to ADB converter

This commit is contained in:
Michel Gallant
2013-01-07 23:47:03 -05:00
commit 51151d9396

476
ps2adb.ino Normal file
View File

@@ -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 <PS2Mouse.h>
#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
}