SuperPad64

  • Idea and Concept: Wolfram Sang
  • Hardware Design: Oliver Tscherwitschke
  • Software: Steffen Görzig, Oliver Tscherwitschke

  • RACE+
  • Space Moguls
  • and more

The SuperPad64 is an adapter for the C64 userport and enables you to connect up to eight SNES game pads.

The complete hardware design of SuperPad64 can be downloaded here.

This includes the schematics and layout in Eagle and PDF format, Gerber PCB data and the bill of material and should be everything that is needed to build yourself a SuperPad64.

Building instructions
Bauanleitung


Implementation in assembler

initAndDetectSnes

First of all, the adapter has to be initialized and detected:

;; initAndDetectSnes: call this routine first one time to detect and initialize the adapter
;; snesDetectionResult: result of the detection. Encoding:
;; 0: no adapter 
;; 1: SuperPad64
snesDetectionResult
!byte $00

initAndDetectSnes:
  lda #$00  		    ; PB = input
  sta $dd03
  lda $dd02
  ora #$04
  sta $dd02     	    ; PA2 = output for latch signal
  lda $dd00
  ora #$04 		    ; PA2 = high
  sta $dd00     		
  and #$fb 		    ; PA2 = high
  sta $dd00     		
  ldx #$0f
initAndDetectSnesLoop:
  lda $dd01		    ; read 16 bytes
  dex
  bpl initAndDetectSnesLoop
  cmp $dd01		    ; byte 16==byte 17?
  beq initAndDetectSnesDone ; yes
  inc snesDetectionResult   ; adapter detected with at least one pad plugged in 
initAndDetectSnesDone:
  rts

After the initialization the input can be read from the pads. The following routines differ between the number of pads and also the number of input buttons. For example „4Pads5Bits“ indicates that the input form the first 4 Pads is read and 5 bits (directions and fire button „B“) analog to a standard joystick input. „8Pads12Bits“ reads from 8 Pads 12 bits (directions and all buttons).

The resulting bit logic is negated, e.g. when a button or a direction is pressed, the according bit is 0 (and not 1).

8Pads12Bits

This is the universal read function to read a given number of bits from all eight gamepads.

tmp = $02       ; temp variable in zero page

; 'pads' stores the button state of all gamepads.
pads    !word 0,0,0,0,0,0,0,0

; CIA Register definitions
CIA2_PRA = $DD00
CIA2_PRB = $DD01
CIA2_DDRA = $DD02
CIA2_DDRB = $DD03


; ===========================================
; =========== Read all 8 gamepads ===========
; Input:  A: Number of bits to read from gamepad
; Output: Array 'pads' (8 x 16-bits): Button states (low-active)
; Uses zero page variable 'tmp'
;
getSnes8PadsNBits
    sta tmp         ; bit counter
    
    ; Generate latch pulse to store button data
    lda CIA2_PRA
    ora #$04
    sta CIA2_PRA    ; Latch = 1
    and #$FB
    sta CIA2_PRA    ; Latch = 0
    
sp64_bitloop 
    lda CIA2_PRB    ; Read data from Port B...
    ldx #0
sp64_padloop        ; ... and shift it in array 'pads'
    lsr             ;
    rol pads,x      ; bits: Up Down Left Right A X L R
    rol pads+1,x    ; bits: x x x x B Y Select Start
    inx
    inx
    cpx #16
    bmi sp64_padloop
    dec tmp
    bne sp64_bitloop
    rts

To read all 12 buttons just call this routine with A set to 12:

lda #12
jsr getSnes8PadsNBits

Now 'pads' contains the button states of each gamepad in the lower 12 bits.
Please note that the bits are low-active, i.e. the bit is '0' if the button is pressed and '1' if not.

Bit 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
Button x x x x B Y Select Start Up Down Left Right A X L R

8Pads8Bits

Variant A

If 8 buttons are enough for your application, just set A to 8 and call the above function. In this case only the lower bytes of the pads array members are relevant
You could save 8 bytes of RAM if you change the 'pads' array to !byte type and commenting out the 'rol pads+1,x' line.

lda #8
jsr getSnes8PadsNBits
Bit 7 6 5 4 3 2 1 0
Button B Y Select Start Up Down Left Right
Variant B

If you prefer the bits to be in the same order as a joystick in one of the control ports would deliver, you can use this special function:

tmp = $02       ; temp variable in zero page

; 'pads' stores the button state of all gamepads.
pads    !byte 0,0,0,0,0,0,0,0

; CIA Register definitions
CIA2_PRA = $DD00
CIA2_PRB = $DD01
CIA2_DDRA = $DD02
CIA2_DDRB = $DD03


; ===========================================
; =========== Read all 8 gamepads ===========
; Input:  None
; Output: Array 'pads' (8 x 8-bits): Button states (low-active)
; Uses zero page variable 'tmp'
;
getSnes8Pads8Bits  
    lda #8    
    sta tmp         ; bit counter
    
    ; Generate latch pulse to store button data
    lda CIA2_PRA
    ora #$04
    sta CIA2_PRA    ; Latch = 1
    and #$FB
    sta CIA2_PRA    ; Latch = 0
    
sp64_8x8_bitloop 
    lda CIA2_PRB    ; Read data from Port B...
    ldx #0
sp64_8x8_padloop    ; ... and shift it in array 'pads'
    lsr             ;
    ror pads,x      ; bits: Right Left Down Up Start Select Y B
    inx
    cpx #8
    bmi sp64_8x8_padloop
    dec tmp
    bne sp64_8x8_bitloop
    
    ; Make bit order compatible to control port
    ; Bits: Start Select Y B Right Left Down Up
    ldx #0
sp64_8x8_fixloop        
    lda pads,x
    ror
    ror pads,x
    ror
    ror pads,x
    ror
    ror pads,x
    ror
    ror pads,x     
    inx
    cpx #8
    bmi sp64_8x8_fixloop    
    
    rts
Bit 7 6 5 4 3 2 1 0
Button Start Select Y B (Fire) Right Left Down Up

8Pads5Bits

	
;;;;;;;;;;;;;;;;
;; 8Pads5Bits ;;
;;;;;;;;;;;;;;;;
result8Pads5Bits
!byte $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff

getSnes8Pads5Bits:
  lda $dd00
  ora #$04 		        ; PA2 = high
  sta $dd00     		
  and #$fb 		        ; PA2 = low
  sta $dd00     		
  lda $dd01                     ; get values for firebutton "B"
  pha		                ; remember firebutton values in heap
  lda $dd01                     ; get and forget values for "Y"
  lda $dd01                     ; get and forget values for "SELECT"
  lda $dd01                     ; get and forget values for "START"
  ldy #$03			; get 4 directions
getSnes8Pads5BitsNextDirection:	
  lda $dd01                     ; get direction values "up", "down", "left", and "right"
  ldx #$07			; store direction in joystick variables
getSnes8Pads5BitsLoop:	
  rol                           ; shift bit left into carry...
  ror result8Pads5Bits,x        ; ...then into its destination
  dex
  bpl getSnes8Pads5BitsLoop
  dey
  bpl getSnes8Pads5BitsNextDirection
                                ; now values in each joy: right, left, down, up, x, x, x, x 
  pla		                ; restore firebutton values from heap to accu
  ldx #$07	                ; add firebutton and generate encoding compatible to
	                        ; standard joystick format:
	                        ;  0,0,0, fire, right, left, down, up
getSnes8Pads5BitsConvertToStandardLoop:	
  rol                           ; shift fire bit to carry ...
  ror result8Pads5Bits,x        ; ...then into its destination
  lsr result8Pads5Bits,x	; fill left three bits with 0
  lsr result8Pads5Bits,x			
  lsr result8Pads5Bits,x
  dex
  bpl getSnes8Pads5BitsConvertToStandardLoop
  rts
Bit 7 6 5 4 3 2 1 0
Button 0 0 0 B (Fire) Right Left Down Up

4Pads8Bits

;;;;;;;;;;;;;;;;
;; 4Pads8Bits ;;
;;;;;;;;;;;;;;;;
result4Pads8Bits
!byte $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff

tmp4Pds8Bits
!byte $00

getSnes4Pads8Bits:
  lda $dd00
  ora #$04 		        ; PA2 = high
  sta $dd00     		
  and #$fb 		        ; PA2 = low
  sta $dd00       
  lda #$07
  sta tmp4Pds8Bits              ; bit counter 7..0
getSnes4Pads8BitsBitLoop:	
  lda $dd01                     ; read data from port b
  ldx #$00
getSnes4Pads8BitsPadLoop:       ; move data into result array
  lsr             
  ror result4Pads8Bits,x        ; bits: right left down up START SELECT Y fire(B)
  inx
  cpx #$04                      ; stop after 4 Pads 
  bmi getSnes4Pads8BitsPadLoop
  dec tmp4Pds8Bits
  bpl getSnes4Pads8BitsBitLoop
  ldx #$00	                ; bring bits to standard joystick format:
			        ; START, SELECT, Y, fire(B), right, left, down, up
getSnes4Pads8BitsStandardLoop:	
  lda result4Pads8Bits,x
  ror
  ror result4Pads8Bits,x
  ror
  ror result4Pads8Bits,x
  ror
  ror result4Pads8Bits,x
  ror
  ror result4Pads8Bits,x 
  inx
  cpx #$04
  bmi getSnes4Pads8BitsStandardLoop
  rts
Bit 7 6 5 4 3 2 1 0
Button Start Select Y B (Fire) Right Left Down Up

4Pads5Bits

;;;;;;;;;;;;;;;;
;; 4Pads5Bits ;;
;;;;;;;;;;;;;;;;
result4Pads5Bits
!byte $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff

getSnes4Pads5Bits:
  lda $dd00
  ora #$04 		       ; PA2 = high
  sta $dd00     		
  and #$fb 		       ; PA2 = low
  sta $dd00     		
  lda $dd01                    ; get values for firebutton "B"
  pha			       ; remember firebutton values in heap
  lda $dd01                    ; get and forget values for "Y"
  lda $dd01                    ; get and forget values for "SELECT"
  lda $dd01                    ; get and forget values for "START"
  ldy #$03		       ; get 4 directions
getSnes4Pads5BitsNextDirection:	
  lda $dd01                    ; get direction values "up", "down", "left", and "right"
  ldx #$00		       ; store direction in joystick variables
getSnes4Pads5BitsShiftBits:	
  lsr                          ; shift bit left into carry...
  ror result4Pads5Bits,x       ; ...then into its destination
  inx
  cpx #$04		       ; stop after 4 pads
  bmi getSnes4Pads5BitsShiftBits
  dey
  bpl getSnes4Pads5BitsNextDirection
			       ; now values in each joy: right, left, down, up, x, x, x, x 
  pla		               ; restore firebutton values from heap to accu
  ldx #$00	               ; add firebutton and generate encoding compatible to
	                       ; standard joystick format:
	                       ; 0,0,0, fire, right, left, down, up
getSnes4Pads5BitsFixLoop:	
  lsr                          ; shift fire bit to carry ...
  ror result4Pads5Bits,x       ; ...then into its destination
  lsr result4Pads5Bits,x       ; fill left three bits with 0
  lsr result4Pads5Bits,x			
  lsr result4Pads5Bits,x
  inx
  cpx #$04		       ; stop after 4 pads
  bmi getSnes4Pads5BitsFixLoop
  rts
Bit 7 6 5 4 3 2 1 0
Button 0 0 0 B (Fire) Right Left Down Up

Test Program

A SuperPad64 test program can be downloaded here.