Monday 11 April 2016

Finally! Joy after getting past my sticking point!

To say that adding joystick support was frustrating is an understatement!

There's not a whole lot of information on the net about sampling the joysticks, less still about reading the buttons, and some of the information that is available is, well, wrong. And to confuse matters further, I was having issues in MESS with the 2nd joystick button. Not having a physical 2-button joystick to test on real hardware is fraught with impediments to success.

Still at this point unable to test on real hardware, I at least seem to have sorted out the details and MESS finally behaves as I'd expect, given the Coco 3 schematics. I guess I should have gone to them in the first place, rather than as a last resort.

I thought I'd post my joystick code here on the off chance that someone else has similar issues in the future.

Let's define some useful constants.

PIA0        .equ    0xFF00
PIA1        .equ    0xFF20
DATAA       .equ    0x00
DDRA        .equ    DATAA
CRA         .equ    0x01
DATAB       .equ    0x02
DDRB        .equ    DATAB
CRB         .equ    0x03

; equates to keyboard rows
; - phantom keys appear accordingly
RJOY_BTN1   .equ    (1<<0)
LJOY_BTN1   .equ    (1<<1)
RJOY_BTN2   .equ    (1<<2)
LJOY_BTN2   .equ    (1<<3)

;.define LEFT_JOYSTICK
.ifdef LEFT_JOYSTICK
  JOY_BTN1  .equ    LJOY_BTN1
  JOY_BTN2  .equ    LJOY_BTN2
.else
  .define RIGHT_JOYSTICK
  JOY_BTN1  .equ    RJOY_BTN1
  JOY_BTN2  .equ    RJOY_BTN2
.endif
; high and low thresholds
; for 'digital' operation
JOY_LO_TH   .equ    0x64                ; ~40%
JOY_HI_TH   .equ    0x98                ; ~60%


First up, the one-off initialisation, which sets all the appropriate PIA data-direction registers. Bear in mind that Knight Lore takes full control of the Coco 3 hardware, swapping out ROM and disabling all interrupts except a simple (F)ISR used to emulate the Z80 Refresh register. So the PIA's are immune to any reconfiguration behind-the-scenes.

  ; configure joystick axis selection as outputs
  ; and also select left/right joystick
        lda     PIA0+CRA
        ldb     PIA0+CRB
        ora     #(1<<5)|(1<<4)          ; CA2 as output
        orb     #(1<<5)|(1<<4)          ; CB2 as output
.ifdef LEFT_JOYSTICK
        orb     #(1<<3)                 ; CB2=1 left joystick
.else
        andb    #~(1<<3)                ; CB2=0 right joystick
.endif
        sta     PIA0+CRA
        stb     PIA0+CRB
  ; configure comparator as input
        lda     PIA0+CRA
        anda    #~(1<<2)                ; select DDRA
        sta     PIA0+CRA
        lda     PIA0+DDRA
        anda    #~(1<<7)                ; PA7 as input
        sta     PIA0+DDRA
        lda     PIA0+CRA
        ora     #(1<<2)                 ; select DATAA
        sta     PIA0+CRA
  ; configure sound register as outputs
        lda     PIA1+CRA
        anda    #~(1<<2)                ; select DDRA
        sta     PIA1+CRA
        lda     PIA1+DDRA
        ora     #0xfc                   ; PA[7..2] as output
        sta     PIA1+DDRA
        lda     PIA1+CRA
        ora     #(1<<2)                 ; select DATAA
        sta     PIA1+CRA


Now the joystick and button sampling. Note that Knight Lore uses a 'digital' joystick so it's only concerned with checking the position on each axis within three zones - left, middle and right.

        ; select hoizontal axis
        lda     PIA0+CRA
        anda    #~(1<<3)                ; CA2=0 horizontal
        sta     PIA0+CRA
        ; set comparator value to 40%
        lda     PIA1+DATAA
        anda    #0x03                   ; clear value
        ora     #JOY_LO_TH              ; low threshold
        sta     PIA1+DATAA
        lda     PIA0+DATAA              ; comparator
        bita    #(1<<7)                 ; joystick greater?
        bne     1$                      ; yes, skip
        orb     #INP_LEFT               ; aka #INP_W
        bra     2$
        ; set comparator value to 60%
1$:     lda     PIA1+DATAA
        anda    #3                      ; clear value
        ora     #JOY_HI_TH              ; high threshold
        sta     PIA1+DATAA
        lda     PIA0+DATAA              ; comparator
        bita    #(1<<7)                 ; joystick greater?
        beq     2$                      ; no, skip
        orb     #INP_RIGHT              ; aka #INP_E
        ; select vertical axis
2$:     lda     PIA0+CRA
        ora     #(1<<3)                 ; CA2=1 vertical
        sta     PIA0+CRA
        ; set comparator value to 40%
        lda     PIA1+DATAA
        anda    #0x03                   ; clear value
        ora     #JOY_LO_TH              ; low threshold
        sta     PIA1+DATAA
        lda     PIA0+DATAA              ; comparator
        bita    #(1<<7)                 ; joystick greater?
        bne     3$                      ; yes, skip
        orb     #INP_FORWARD            ; aka #INP_N
        bra     4$
        ; set comparator value to 60%
3$:     lda     PIA1+DATAA
        anda    #3                      ; clear value
        ora     #JOY_HI_TH              ; hi threshold
        sta     PIA1+DATAA
        lda     PIA0+DATAA              ; comparator
        bita    #(1<<7)                 ; joystick greater?
        beq     4$                      ; no, skip
        orb     #INP_PICKUP_DROP        ; aka #INP_S
        ; read joystick buttons
4$:     lda     #0xff                   ; no keys, only buttons
        jsr     read_port
        bita    #JOY_BTN1
        beq     5$
        orb     #INP_JUMP
5$:     bita    #JOY_BTN2
        beq     6$
        orb     #INP_DIR_PICKUP_DROP
6$:     bra     finished_input               


I haven't done any timing nor studied the BASIC joystick routines but short of hand-picking registers and instructions that minimise clock cycles, I don't think there's much you could do to get a faster sampling routine. Happy to be proven wrong by any astute reader.

Waiting for a 2-button adapter to turn up; hopefully that'll arrive in time for me to test before sending the final version off to John for demonstrating at the CocoFEST. I was going to try to experiment with some optimisation to speed up the rendering, but I think I'll take a short break from Knight Lore - or the 6809 port at least - until after CocoFEST.

I'm already thinking about the next port, which is dangerous before finishing off Knight Lore completely. But I've got a couple of new ideas which I need to investigate further. One is an actual arcade port; not a new game for the Coco (or any platform really) but it would be cool to have the actual arcade code and graphics, effectively, running on the Coco3. And reverse-engineering is already complete, so the port would be relatively quick. We'll see...

No comments:

Post a Comment