Source Code


Files

Nintendo 64 Polling
Include file for N64 Polling
Nintendo to PS/2 Packet Translation
PS/2 Host Emulator used in debugging
Archive of all source + schematic + docs
[EE 476 Homepage | Home | Previous: Hindsight | Next: Schematics]

n64.asm

;*******************************************************************************
;N64 ASM	Joseph Weaver, jew17@cornell.edu
;Atmel 90S1200 code to poll a nintendo 64 controller 
;and report the response in 4 selectable words on port B
;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
;
;
;******************************************************************************



;********************************
.nolist
.include "AT90S1200def.inc"
.list


;********************************
;define registers
.def	Temp	=r16	;temporary register
.def	Temp2	=r23	;second temporary register
.def	WordRq	=r17	;Word Requested
.def	Word0	=r18	;first 8 bits
.def	Word1	=r19	;second
.def	Word2	=r20	;3rd
.def	Word3	=r21	;4th
.def	PollEn	=r24	;Poll sheduling
.def	Save	=r25	;SREG saving
.def	Reload	=r26



;********************************
;set up macros here

	.MACRO __DELAY_USB
	ldi  R27,LOW(@0)
__DELAY_USB_LOOP:
	dec  R27
	brne __DELAY_USB_LOOP
	.ENDM

;*********************************
;set up the interrupt vectors
;Vector	Address	Source		Interrupt Definition
;1	$000	RESET		Hardware Pin and Watchdog Reset
;2	$001	INT0		External Interrupt Request 0
;4	$002	TIMER0,OVF0	Timer/Counter0 Overflow
;5	$003	ANA_COMP	Analog Comparator

;intialize the code
.cseg
.org $0000
	rjmp RESET	;reset entry vector
	reti		
	RJMP Timer
	reti

RESET:
;clear all the registers
	clr Temp	;initialize variables
	clr Temp2
	clr Word0
	clr Word1
	clr Word2
	clr Word3
	clr WordRq
	clr PollEn
	clr Save
	clr Reload

;set up the PORTS
	ldi Temp, 0b1000001
	out DDRD, Temp	;PORTD 0,6 is output, 1-5 input
	ser Temp
	out DDRB, Temp	;PORTB is an output

	;set up timer 0 for 1 mSec ticks
	ldi	Temp, 0b00000010
	out	TIMSK, Temp	;enable interrupt
	ldi 	Temp, 0b00000100		
	out 	TCCR0, Temp	;prescale at CK/256
	ldi	Reload,55	
	out 	TCNT0, Reload	;timer overflows every 200 ticks = 2.56 ms

	;start with polling disabled
	clr	PollEn
	
	cbi PORTD,6
	;enable watchdog at 16ms
	ldi Temp,0b00001000
	out WDTCR,Temp
	
	;Start the clock ticking
	sei			;enable all interrupts

	
;infinite loop
ILOOP:
	wdr	;reset the watchdog timer


;*******************
;Word transfer block
;*******************
	;get the current word request off PORTD.2 and PORTD.3
	clr WordRq
	sbic PIND,3
	ori WordRq, 0b00000001
	lsl WordRq
	sbic PIND,2
	ori WordRq, 0b00000001

	;assert the proper word on PortB
	CPI WordRq, 0
	BRNE SHOWW1
	OUT PORTB, Word0	
SHOWW1: CPI WordRq, 1
	BRNE SHOWW2
	OUT PORTB, Word1
SHOWW2:	CPI WordRq, 2
	BRNE SHOWW3
	OUT PORTB, Word2
SHOWW3:	CPI WordRq, 3
	brne ENDRQ
	OUT PORTB, Word3
ENDRQ:
	
;	ldi Temp2, 0b10110100
;	out PORTB, Temp2
;	out PORTB, Word0
	TST PollEn
	BREQ ILOOP	;don't poll, unless enabled


;*******************
;Polling block
;*******************
	clr Temp2
	clr PollEn	;reset poll enable for next interrupt
	
;assert 7 nintendo logic 0's to the pulldown circuit
OUT0:	
	sbi PORTD, 0
	__DELAY_USB 18
	cbi PORTD, 0
	__DELAY_USB 7

	inc Temp2
	cpi Temp2, 7
	brne OUT0
	clr Temp2
	clr Temp

;assert 2 nintendo logic 1's to pulldown circuit
;note that it takes less to do this explicitly twice than loop
;also final delay is not needed, line will float high
OUT1:
	sbi PORTD, 0
	__DELAY_USB 7
	cbi PORTD, 0
	__DELAY_USB 18

	sbi PORTD, 0
	__DELAY_USB 7
	cbi PORTD, 0

;slurp data
;note: we don't think there's enough time between bits to select which word we 
write to, so we do 4 explicit loops
;	it should still fit in ram.
;TODO if time, test to see if we can fit it
	clr Temp
	clr Temp2
	__DELAY_USB 13	;wait a bit for response

	sbi PORTD,6
LISTEN:
	sbic PIND,1	;wait for bit
	rjmp LISTEN
	__DELAY_USB 13	;figure out bit value
	sbis PIND,1
	rjmp ZDETCT0	
	ori Temp,0b00000001
	rjmp ZD0
ZDETCT0:
	andi Temp,0b11111110

ZD0:	
	cpi Temp2, 7	;load value into temp
	breq LP
	lsl Temp
LP:	sbis PIND,1
	rjmp LP	
	inc Temp2
	cpi Temp2, 8	;read 8 bits of data
	brne LISTEN
	
	mov Word0, Temp	;put bits into correct register
	clr Temp2


;we do the above for the nex 3 registers.  perhaps we could fit them into a 
loop, but we
;don't want to mess with timing, and it fits, and there's 3 hours left in lab
LISTEN1:
	sbic PIND,1
	rjmp LISTEN1
	__DELAY_USB 13
	sbis PIND,1
	rjmp ZDETCT1	
	ori Temp,0b00000001
	rjmp ZD1
ZDETCT1:
	andi Temp,0b11111110

ZD1:	
	cpi Temp2, 7
	breq LP1
	lsl Temp
LP1:	sbis PIND,1
	rjmp LP1	
	inc Temp2
	cpi Temp2, 8
	brne LISTEN1
	
	mov Word1, Temp
	clr Temp2


LISTEN2:
	sbic PIND,1
	rjmp LISTEN2
	__DELAY_USB 13
	sbis PIND,1
	rjmp ZDETCT2
	ori Temp,0b00000001
	rjmp ZD2
ZDETCT2:
	andi Temp,0b11111110

ZD2:
	cpi Temp2, 7
	breq LP2
	lsl Temp
LP2:	sbis PIND,1
	rjmp LP2
	inc Temp2
	cpi Temp2, 8
	brne LISTEN2

	mov Word2, Temp
	clr Temp2

LISTEN3:
	sbic PIND,1
	rjmp LISTEN3
	__DELAY_USB 13
	sbis PIND,1
	rjmp ZDETCT3	
	ori Temp,0b00000001
	rjmp ZD3
ZDETCT3:
	andi Temp,0b11111110

ZD3:	
	cpi Temp2, 7
	breq LP3
	lsl Temp
LP3:	sbis PIND,1
	rjmp LP3	
	inc Temp2
	cpi Temp2, 8
	brne LISTEN3
	
	mov Word3, Temp
	;out PORTB, Temp
	clr Temp2
	cbi PORTD,6
	wdr
	
	rjmp ILOOP	;loop forever



;timer 0 ISR (timer-zero overflow)
;Enters every 1.0 mSec

TIMER:	in	Save, SREG
	out 	TCNT0, Reload	;save state and reaload counter
;	ldi Temp, 0b11110000
;	out PORTB, Temp
	ser	PollEn		;enable poll
	out	SREG, save	;restore state
	reti			;back to backgound

ps2.c

/*******************************************************************************
*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:

		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

hostutils.c

/*******************************************************************************
*hostutils.c	Robert Buels, rmb32@cornell.edu
*ps/2 host emulator, useful in debuggings ps2.c
*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 


#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 131
unsigned char buttonState;
unsigned char deltaX, deltaY, deltaZ;

unsigned char hostCommand,mouseResponse;

unsigned char sendResult;
char transmitMode;

unsigned char sampleTime,sampleReload;
                   
#pragma regalloc+

void receiveByte();
void sendData();
void sendByte();
void processCommand();
void readN64();

void init() {
   PORTA = 0xff;
   DDRA = 0x00;  //port A: PS2 in-out
   DDRB = 0xff;  //port B: control to 1200
   DDRC = 0x00;
   DDRD = 0x00;  //port D: input from 1200
   
   
   sampleTime = 10;
   sampleReload = 10;
   
  // buttonState = deltaX = deltaY = 0b01001011;
}

void main() {
	init();         

	while(1) {            
 /*
		#asm("__CLEARCLOCK")
		while(!(~PIND)) PORTB=~0x01;
		#asm("__CLEARDATA")
		delay_us(84);
		#asm("__SETCLOCK")
		delay_us(5);
		while(PINA.1) {
			PORTB=~0x0A;
		}
		#asm("__SETDATA")
   */   
		PORTB=~0x01;   
		while(!(~PIND)) {}
		PORTB=~0xFF;
		switch (~PIND) {
			case 0x01:  hostCommand = 0xFF; break;  //reset
			case 0x02:  hostCommand = 0xF4; break;  //enable data reporting
			case 0x04:  hostCommand = 0xF5; break;  //disable data reporting
			case 0x08:  hostCommand = 0xF3; break;  //set sample rate
			case 0x10:  hostCommand = 0xE8; break;  //get device ID
			case 0x20:  hostCommand = 0x00; break;	//200
			case 0x40:  hostCommand = 0x01; break;  //100
			case 0x80:  hostCommand = 0x02; break;  //80
			default: hostCommand = 0xFF; break;
		}
		sendByte();
			    	delay_ms(500);
	}
}


void sendByte() {
	#asm("__CLEARCLOCK")
	delay_ms(200);
	#asm
	lds output,_hostCommand
	__CLEARDATA
	__DELAY_USB 112   ; delay 84 us like the Dell computers in the lab
	__SETCLOCK

	ldi delay,0xFF
	out PORTB,delay

	mov temp,output

	ldi temp2,8
ByteOutLoop:
	rcall __WaitForClockLow
	rcall __OutBit
	lsr temp
	rcall __WaitForClockHigh
	dec temp2
	brne ByteOutLoop
 
    	;; 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 temp,parity			; now lsb of parity is even parity
        com temp                ; invert it to get odd parity
  rcall __WaitForClockLow       
  rcall __OutBit				; send our parity
  rcall __WaitForClockHigh
  rcall __WaitForClockLow
  rjmp ByteOutEnd

;;outputs a single bit to the ps2 line as a host would
__OutBit:               
	sbrs temp,0
	rjmp OutBitClear
	rjmp OutBitSet
OutBitClear:
	__CLEARDATA
	rjmp OutBit2:
OutBitSet:
	__SETDATA
OutBit2:	
        ret

ByteOutEnd:
	__SETDATA
	#endasm
}
    

#asm

    __WaitForClockLow:
    	__DELAY_USB 7	; 5 us
	WaitForClockLowLoop:
		sbic PINA,1		
		rjmp WaitForClockLowLoop
		ret	
	
    __WaitForClockHigh:
    	__DELAY_USB 7	; 5 us
	WaitForClockHighLoop:
		sbis PINA,1
		rjmp WaitForClockHighLoop
				ret
#endasm

[EE 476 Homepage | Home | Previous: Hindsight | Next: Schematics]