So I decided to take a look at the source code for Kupid 2.0, being as I haven't looked at it in five or so months. I've got a lot of re-learning to do before I can continue.
Actually, I'm thinking now that if I start working on 2.0 again, I'm going to reevaluate my design. Assembly is terribly unforgiving, and I don't really have too much time to spend dealing with it. Maybe I'll try out C... I do have a shiny new chip programmer I never got around to using...
The latest version of the source code is copy/pasted below. I wasn't going to release it until I was totally done, but what the heck. The comments aren't 100% accurate - it was a work in progress after all.
If anyone wants to know how to program an Atmel AVR microcontroller to emulate a PS1/2 controller in assembly language, then jackpot.
Code:
.NOLIST ; Don't list the following in the list file
.INCLUDE "tn2313def.inc" ; Import of the file
.LIST ; Switch list on again
.CSEG
.ORG $0000
init:
; Init stack
LDI r16, LOW(RAMEND) ; Lower byte
OUT SPL, r16 ; to stack pointer
;Init USI Control Register
LDI r16, (1<<USIWM0) | (1<<USICS1) ;| (1<<USICS0) ;put the initialization data into register 16
OUT USICR, r16 ;take the data out of register 16 and initialize the USI Control Register
;Init Port B
LDI r16,(0<<DDB7)|(1<<DDB6)|(0<<DDB5) | (1<<DDB4) | (0<<DDB3) ;direction of Port B
OUT DDRB, r16
LDI r17, 0b00000000 ;init register17 which is the BYTE counter
LDI r18, (1<<PB4) ;store acnkowledge signal in register 18
LDI r19, 0b00000001
OUT PORTB, r18 ;set ack signal high
SlaveSPITransferOut:
RJMP SlaveSPITransferIn
SendByteTest1:
LDI r16, 0b10011001 ;clear register 16 for sending the first blank byte
OUT USIDR, r16 ;copy register 16 to the USI Data Register to send a reply
RJMP OverflowReset
SendByteTest2:
LDI r16, 0b10000001 ;clear register 16 for sending the first blank byte
OUT USIDR, r16 ;copy register 16 to the USI Data Register to send a reply
RJMP OverflowReset
SendCapturedByte:
OUT USIDR, r16 ;copy register 16 to the USI Data Register to send a reply
RJMP OverflowReset
SendByte1:
LDI r16, 0b00000000 ;clear register 16 for sending the first blank byte
OUT USIDR, r16 ;copy register 16 to the USI Data Register to send a reply
RJMP OverflowReset
SendByte2:
LDI r16, 0b10000010 ;put byte x41 into register 16
OUT USIDR, r16 ;copy register 16 to the USI Data Register to send a reply
RJMP OverflowReset
SendByte3:
LDI r16, 0b01011010 ;put byte x5A into register 16
OUT USIDR, r16 ;copy register 16 to the USI Data Register to send a reply
LDI r17, 0b00000001 ;add 1 to r17, this is the BYTE counter
RJMP OverflowReset
SendByte4:
COM r19
SBRS r19, 1
LDI r16, 0b00000001 ;put button byte 1 byte into register 16 - SLCT NULL NULL STRT U R D L
SBRC r19, 1
LDI r16, 0b00000000 ;put button byte 1 byte into register 16
OUT USIDR, r16 ;copy register 16 to the USI Data Register to send a reply
RJMP OverflowReset
SendByte5:
SBRS r19, 1
LDI r16, 0b00000000 ;put button byte 1 byte into register 16 - L2 R2 L1 L2 /\ O X |_|
SBRC r19, 1
LDI r16, 0b00000000 ;put button byte 1 byte into register 16
OUT USIDR, r16 ;copy register 16 to the USI Data Register to send a reply
RJMP OverflowReset
OverflowReset:
;reset the Counter Overflow Interrupt Flag
LDI r16, (1<<USIOIF) ;copy the Counter Overflow Interrupt Flag to register 16
OUT USISR, r16 ;copy the bit stored in register 16, bit 6 of USISR, which should be set
;to 1, to bit 6 of USISR which will reset the Counter Overflow Interrupt Flag to 0
LDI r16, 0b00000000 ;copy the Counter Overflow Interrupt Flag to register 16
INC r17
RCALL DelayLong
RCALL Acknowledge ;send acknowledge signal
SlaveSPITransferIn:
SBIC PORTB, 3 ;skip next instruction if the ATTN line is LOW
RCALL ResetSPI
SBIS USISR, USIOIF ;skip next instruction if the Counter Overflow Interrupt Flag in the USI Status Register is set to 1, as that mean that the entire byte has been recieved from the PlayStation
RJMP SlaveSPITransferIn ;go to the beginning of the transfer loop
CPI r17, 0b00000010 ;compare r17 (byte counter) to 3, if equal, then zero-flag in status register is set to one.
BREQ SendByte4
CPI r17, 0b00000011 ;compare r17 (byte counter) to 4, if equal, then zero-flag in status register is set to one.
BREQ SendByte5
IN r16, USIDR ;copy the contents of the USI Data Register to register 16
CPI r16, 0b00000000 ;compare r16 (which has the data from the SPI data register) to the first expected byte from the Playstation, if equal, then zero-flag in status register is set to one.
BREQ SendByte1
CPI r16, 0b10000000 ;compare r16 (which has the data from the SPI data register) to the second expected byte from the Playstation, if equal, then zero-flag in status register is set to one.
BREQ SendByte2
CPI r16, 0b01000010 ;compare r16 (which has the data from the SPI data register) to the third expected byte from the Playstation, then zero-flag in status register is set to one.
BREQ SendByte3
RJMP SlaveSPITransferOut
;define subroutines
ResetSPI:
LDI r16, (1<<USIOIF) | (0<<USICNT3) | (0<<USICNT2) |(0<<USICNT1) | (0<<USICNT0) ;copy the Counter Overflow Interrupt Flag to register 16
OUT USISR, r16 ;copy the bit stored in register 16, bit 6 of USISR, which should be set, and reset the counter
;to 1, to bit 6 of USISR which will reset the Counter Overflow Interrupt Flag to 0
LDI r16, 0b00000000
OUT USIDR, r16 ;copy register 16 to the USI Data Register to send a reply
LDI r17, 0b00000000 ;reset byte counter
RET
DelayShort: ;do a short delay
NOP
INC r18 ;add 1 to r18
CPI r18, 0b00001000 ;compare r18 to a number, if equal, then zero-flag in status register is set to one.
BRNE DelayShort
LDI r18, 0b00000000
RET ; return to the caller
DelayLong: ;do a long delay
NOP
INC r18 ;add 1 to r18
CPI r18, 0b00010100 ;compare r18 to a number, if equal, then zero-flag in status register is set to one.
BRNE DelayLong
LDI r18, 0b00000000
RET ; return to the caller
Acknowledge: ;ACKnowledge on PORT B pin 4
LDI r18, (0<<PB4) ;store acnkowledge signal in register 18
OUT PORTB, r18 ;start acknowledge signal
RCALL DelayShort
LDI r18, (1<<PB4) ;store acnkowledge signal in register 18
OUT PORTB, r18 ;end acknowledge signal
RET ;return to the caller
I had the basic code programmed in a day or two, but spent a week trying to figure out why it wasn't working at all. It turned out that whenever I went to test the code, I rested my hand on a metal portion of the breadboard I was using - the grounding in my house is so bad, that it caused some kind of feedback. Once I stood about a foot away from it, the code worked perfectly. >_<
So... yeah... Anybody has any questions, you're welcome to ask. I'm sure everyone is dying to know what all the different registers do.
![Razz :P](./images/smilies/tongue_emote.gif)
The RPGT4 video series will probably get worked on first, before Kupid, fyi.