User Tools

Site Tools


base:multi-adapter_support

Multi-Adapter Support

By Sokrates

Several joystick/joypad adapters were available for the C64 (see also here: https://en.wikipedia.org/wiki/Commodore_64_joystick_adapters). Some are even available factory-new today.

In the following it is described how to apply an automatic detection and query for three specific adapters in order to avoid manual configuration, as used in the game “RACE+”. The adapters are:

  • SuperPad64
  • Inception
  • 4-player adapter (protovision/icomp)

Since the 4-player adapter is also supported in the VICE emulator, the multi-adapter code can be used for up to 8 players directly on the C64, and for up to 4 player on the PC.

I don't know a way to detect the 4-player adapter. But fortunately this is not necessary in this constellation, because on the one hand the other two adapters can be detected. On the other hand, the query of the additional two ports of the 4-player adapter does not generate any input for these additional ports in the absence of the adapter. This means that the 4-player adapter query routine can also be used for the two joysticks in the standard control ports if there is no adapter connected at all.

Detection is done in the following steps:

  1. SuperPad64 detected? If yes, initialize and use it. If not, continue with 2.
  2. Inception detected? If yes, initialize and use it. If not, continue with 3.
  3. Initialize and use the 4-player adapter

The detection routine of the first two adapters works correctly if no adapter is connected. The other way around, it could theoretically happen that another connected device is mistakenly recognized as an adapter (no case known so far).

The prioritization from a player's perspective:

  1. SuperPad64
  2. Inception
  3. 4-player adapter and standard control ports
  4. Only standard control ports

If more than one adapter are connected, the adapter with the higher priority is taken (for example if SuperPad64 and the Inception adapter are both connected, then only the SuperPad64 can be used). If no adapter is connected, the standard control ports can be used.

Source Code:

;; multi-adapter handling test code
DETECTION     = $0400       ; so you can see, if something happens
OUTLINE0      = $0450
OUTLINE1      = OUTLINE0 + $28
OUTLINE2      = OUTLINE1 + $28
OUTLINE3      = OUTLINE2 + $28
OUTLINE4      = OUTLINE3 + $28
OUTLINE5      = OUTLINE4 + $28
OUTLINE6      = OUTLINE5 + $28
OUTLINE7      = OUTLINE6 + $28
ZEROPAGE_TMP_LO = $FB	
ZEROPAGE_TMP_HI = $FC

;; generate BASIC Header
BASIC_START = $0801
CODE_START = $080d

* = BASIC_START
!byte 12,8,0,0,158
!if CODE_START >= 10000 {!byte 48+((CODE_START/10000)%10)}
!if CODE_START >= 1000 {!byte 48+((CODE_START/1000)%10)}
!if CODE_START >= 100 {!byte 48+((CODE_START/100)%10)}
!if CODE_START >= 10 {!byte 48+((CODE_START/10)%10)}
!byte 48+(CODE_START % 10),0,0,0

* = CODE_START
main
  jsr Screen_init
  jsr Adapter_init
  jsr Draw_detection
main_loop
  jsr Inception_wait		
  jsr Adapter_read
  jsr Draw_lines
  jmp main_loop

;; some time must pass by until the inception adapter can be read again
;; otherwise there might be wrong results
Inception_wait:
  lda joystickAdapter
  cmp #JOYSTICK_ADAPTER_INCEPTION
  bne Inception_waitIsNoInception
  ldy #$20
  ldx #$00
Inception_waitLoop:
  nop
  dex
  bne Inception_waitLoop
  dey
  bne Inception_waitLoop
Inception_waitIsNoInception:
  rts
  
	
	
outLineHi
!byte >OUTLINE0, >OUTLINE1, >OUTLINE2, >OUTLINE3, >OUTLINE4, >OUTLINE5, >OUTLINE6, >OUTLINE7 
outLineLo
!byte <OUTLINE0, <OUTLINE1, <OUTLINE2, <OUTLINE3, <OUTLINE4, <OUTLINE5, <OUTLINE6, <OUTLINE7 
drawBitsTmp
!byte $00
	
Draw_bits:
  ldx #7
  stx drawBitsTmp         ; Bit counter 7..0
Draw_bitsLoop:
  asl				; set carry flag
  tax				; x = a
  lda #$30			; zero
  bcc Draw_bitsIsActive
  lda #$31			; one
Draw_bitsIsActive:	
  sta (ZEROPAGE_TMP_LO),y
  iny 
  txa				; a = x
  dec drawBitsTmp
  bpl Draw_bitsLoop
  rts
	
drawLineTmpX
!byte $00
drawLineTmpLoop
!byte $00

Draw_detection
  lda #$30			; char "0"
  clc
  adc joystickAdapter
  sta DETECTION
  rts
	
Draw_lines:
  lda #7
  sta drawLineTmpLoop
  lda #$00
  sta drawLineTmpX
Draw_linesLoop:
  ldy #$00
  ldx drawLineTmpX
  lda outLineLo, x
  sta ZEROPAGE_TMP_LO
  lda outLineHi, x
  sta ZEROPAGE_TMP_HI
  lda joystickInput,x
  jsr Draw_bits			; destroys x register
  inc drawLineTmpX
  dec drawLineTmpLoop
  bpl Draw_linesLoop
  rts

	
Screen_init:	
  jsr $e544    ; clear screen
  ldx #$00     ; black and counter
  stx $d021    ; set background 
  stx $d020    ; set border 
  lda #$0f     ; grey 
Screen_fillColorRam:	
  sta $d800,x  
  sta $d900,x
  sta $da00,x
  sta $db00,x
  inx           
  bne Screen_fillColorRam     
  rts

	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; copy from here on for the multi adapter handling ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	
;; Adapter_init: call this routine first one time to detect and initialize the adapters
;; joystickAdapter: result of the initialization routine. Encoding:
;; 0: no adapter or 4 player adapter
;; 1: Inception
;; 2: SuperPad64
;; When multiple adapters are connected at the same time, the adapters are used in the following order:
;; 1: SuperPad64
;; 2: Inception
;; 3: 4 player adapter
	
;; Adapter_read: routine to read the joystick input
;; joystickInput: result of the read routine. 8 bytes, one for each joystick. Encoding:
;; Bit 0: up
;; Bit 1: down
;; Bit 2: left
;; Bit 3: right
;; Bit 4: fire
;; Bits 5-7: do not use, values depend on used adapter
;; Please keep in mind: when button/direction is pressed, the according bit is 0 (and not 1)  

JOYSTICK_ADAPTER_NONE_OR_4_PLAYER = $00
JOYSTICK_ADAPTER_INCEPTION        = $01
JOYSTICK_ADAPTER_SUPERPAD64       = $02

joystickAdapter
!byte JOYSTICK_ADAPTER_NONE_OR_4_PLAYER

joystickInput
!byte $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff

Adapter_init:
  jsr Adapter_init_SuperPad64
  lda joystickAdapter
  bne Adapter_initDetectionDone ; is value not JOYSTICK_ADAPTER_NONE_OR_4_PLAYER any more?
	               ; (==SuperPad64 detected)
  jsr Adapter_init_Inception
  lda joystickAdapter
  bne Adapter_initDetectionDone ; is value not JOYSTICK_ADAPTER_NONE_OR_4_PLAYER any more?
	               ; (==Inception detected)
  jsr Adapter_init_4Player ; init 4-player adapter (even when not connected)
Adapter_initDetectionDone:
  ldx #$07
  lda #$ff
Adapter_initFillLoop:
  sta joystickInput,x
  dex
  bpl Adapter_initFillLoop
  rts

;; detect SuperPad64: read byte16 and byte17. Cases:
;; * byte16!=byte17: SuperPad64 adapter detected and at least 1 pad plugged in
;; * else: no SuperPad64 adapter detected or no pad plugged in
Adapter_init_SuperPad64:
  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
Adapter_init_SuperPad64Loop:
  lda $dd01			; read 16 bytes
  dex
  bpl Adapter_init_SuperPad64Loop
  cmp $dd01			; byte 16==byte 17?
  beq Adapter_init_SuperPad64Done		; yes
  ldy #JOYSTICK_ADAPTER_SUPERPAD64
  sty joystickAdapter		; detected with at least one pad plugged in 
Adapter_init_SuperPad64Done:
  rts

Adapter_init_Inception:
  jsr Adapter_read_Inception
  ldy #$00
  lda joystickInput,y
  and #$e0
  bne Adapter_init_InceptionDone
  lda #JOYSTICK_ADAPTER_INCEPTION ; adapter detected 
  sta joystickAdapter	
Adapter_init_InceptionDone:
  rts
	
Adapter_init_4Player:
  lda #$80	       
  sta $DD03 ; CIA2 PortB Bit7 as OUT
  lda $DD01 ; force Clock-Stretching (SuperCPU)
  sta $DD01 ; and release Port
  rts
	
	
Adapter_read:
  lda joystickAdapter
  bne Adapter_readIsSuperPad64OrInception ; jump if not JOYSTICK_ADAPTER_NONE_OR_4_PLAYER = $00
  jsr Adapter_read_4Player
  rts
Adapter_readIsSuperPad64OrInception:
  cmp #JOYSTICK_ADAPTER_SUPERPAD64
  bne Adapter_read_Inception ; jump for inception
  jsr Adapter_read_SuperPad64
  rts
Adapter_readIsInception:
  jsr Adapter_read_Inception
  rts
	
Adapter_read_SuperPad64:	
  lda $dd00
  ora #$04 		        ; PA2 = high
  sta $dd00     		
  and #$fb 		        ; PA2 = low
  sta $dd00     		
  lda $dd01     ; get values for firebutton "b"
  ;eor #$ff	; invert bits
  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
Adapter_read_SuperPad64NextDirection:	
  lda $dd01     ; get direction values "up", "down", "left", and "right"
  ;eor #$ff	; invert bits
  ldx #$07			; store direction in joystick variables
Adapter_read_SuperPad64BitsToJoysticks:	
  rol                           ; shift bit left into carry...
  ror joystickInput,x                     ; ...then into its destination
  dex
  bpl Adapter_read_SuperPad64BitsToJoysticks
  dey
  bpl Adapter_read_SuperPad64NextDirection
  ;; 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
Adapter_read_SuperPad64ConvertToStandardLoop:	
  rol                           ; shift fire bit to carry ...
  ror joystickInput,x           ; ...then into its destination
  lsr joystickInput,x		; fill left three bits with 0
  lsr joystickInput,x			
  lsr joystickInput,x
  dex
  bpl Adapter_read_SuperPad64ConvertToStandardLoop
  rts


inceptionJoystickTmp
!byte $00

; 8-Player Adapter Inception
; wait by using NOPs is needed for timing
Adapter_read_Inception:
  lda #$00 ; #init_opcode
  sta $DC00 
  lda #$1f 
  sta $DC02 
  nop
  nop
  nop
  lda #$10 
  sta $DC00 
  sta $DC02 
  ldx #$00 
  nop				
  nop
  nop
  nop
  nop
  nop
Adapter_read_InceptionLoop:
  lda $DC00 
  asl 
  asl 
  asl 
  asl 
  ldy #$00 
  sty $DC00 
  sta inceptionJoystickTmp ; sta rcv+1
  nop
  nop
  nop
  nop
  nop
  nop
  lda $DC00 
  ldy #$10 
  sty $DC00 
  and #$0f 
  ora inceptionJoystickTmp ; rcv: ora #$00
  eor #$1f ; use only when JOY are required negated (like direct read)
  sta joystickInput,x
  inx 
  cpx #$08 ; data_length
  bne Adapter_read_InceptionLoop
  lda #$7F ;joy_def
  sta $DC00 
  lda #$FF 
  sta $DC02 
  rts	

	
Adapter_read_4Player:	
  lda $DC01 ; read Port1
  ;and #$1F
  sta joystickInput+$00

  lda $DC00 ; read Port2
  ;and #$1F
  sta joystickInput+$01

  lda $DD01 ; CIA2 PortB Bit7 = 1
  ora #$80
  sta $DD01

  lda $DD01 ; read Port3
  ;and #$1F
  sta joystickInput+$02

  lda $DD01 ; CIA2 PortB Bit7 = 0
  and #$7F
  sta $DD01

  lda $DD01 ; read Port4
  tax  ; x = a
  and #$0F
  sta joystickInput+$03
  txa ; a = x
  and #$20
  lsr
  ora joystickInput+$03
  sta joystickInput+$03
  rts	
base/multi-adapter_support.txt · Last modified: 2017-08-09 21:22 by sokrates