On Wednesday, 19 February 2014 14:43:54 UTC-6, D Finnigan wrote:
> Dagen Brock wrote:
> > It worked ok, but I see two issues.
> > 1) When I poll the joystick in basic reading PDL(0) and immediately after,
> > PDL(1), it starts to have a weird effect of not being able to hit max
> > values. If I put in a delay, it works fine. I don't think this happens
> > with my "genuine" Apple joystick so I assume I've somehow screwed up the
> > timing with the capacitor.
> That's a long-known and well-documented side-effect. Search around for
> "simultaneous paddle reading". The workaround, as you discovered, is to
> introduce a delay between readings.>
Some notes and programs I came across which includes reading 2 paddles at the same time, extended range and paddle jitter. I was attempting to combine them.
Reading Two Paddles at the Same Time
You may have discovered by now that if you try to read both game paddles from BASIC, there is some interaction at certain ranges. The problem is that there is only one trigger for both (really, all four) analog ports. If one of them times out long enough before the other one, you will read the tail end of the count on the second timer.
I wrote a little subroutine which reads both paddles at once, eliminating all interaction. It also stretches the range, meaning you need a higher resistance than the standard paddles to get a full 0-255 counting range. Programs which use both paddles will run faster using this subroutine, because you get two readings in the time of one.
Conquering Paddle Jitter
A well-known problem with the paddles supplied with the Apple (at least they USED to be supplied!) concerns their tendency to rock back and forth between two adjacent values. "Jittering" like this can cause problems unless accuracy is unimportant, or unless the effect is somehow pleasing.
One solution to the jitter problem is to force the new paddle reading to move at least two increments from the prior reading. This works, but at the price of lower resolution. Also, it can have subtle side-effects.
A better solution is to keep track of the previous direction of movement, and enforcing the "rule of two" only if the direction is reversed.
The following program demonstrates my solution. It is set up to work with Applesoft, but it would be rather simple to make it directly callable from your own assembly language routines. To use from Applesoft, POKE the paddle number (0-3) at 768, CALL 770, and read the paddle value with PEEK(769).
I set up the following Applesoft program to test the routine, and to compare it with normal paddle readings:
10 POKE 768,0:CALL 770:PRINT PEEK(769):GOTO10
20 PRINT PDL(0):GOTO20
I typed RUN 20 and set the paddle to a jittery position. Then I typed control-C and RUN 10 to test the smoothing subroutine. The program really works!
*--------------------------------
* READ BOTH GAME PADDLES AT THE SAME TIME
* and extended paddle range
*--------------------------------
KEYBOARD .EQ $C000
*--------------------------------
TEST JSR READ.BOTH.PADDLES
TYA ; (Y) = PDL 1 SETTING
JSR $FDDA ; PRINT IN HEX ON SCREEN
INC $24
TXA ; (X) = PDL 0 SETTING
JSR $FDDA ; PRINT IN HEX ON SCREEN
LDA #0 ; HTAB 1
STA $24
LDA $C000
BPL TEST ; NO KEYPRESS, KEEP READING PADDLES
STA $C010 ; CLEAR KEYBOARD STROBE
RTS ; RETURN
READ.BOTH.PADDLES EQU *
LDX #0 ; PADDLE 0 & 1
* Enter here with x=0 for pdls 0 & 1 or x=2 for pdls 2 & 3
LDA $C070 ; START THE PADDLE TIMERS
]lp LDA $C064,X ; CHECK PADDLE 0 TIMER
BPL pdl1 ; TIMED OUT
inx
INC $6 ; COUNT PDL0
LDA $C064.x ; CHECK PADDLE 1
BPL pdl0 ; TIMED OUT
dex
INC $8 ; COUNT PDL1
BNE ]lp ; AGAIN
INC $7 ; INCREMENT HIGH BYTE COUNTER FOR X
INC $9 ; FOR Y
BPL ]lp ; ALWAYS
*---PADDLE 0 TIMED OUT, KEEP LOOKING AT PADDLE 1
pdl1 inx
LDA $C064,X ; CHECK PADDLE 1
BPL jit3 ; TIMED OUT
INC $6 ; COUNT PDL1
NOP ; EQUALIZE TIMING
NOP
NOP
BNE pdl1+1
LDA $7
BNE .3
INC $7 ; INCREMEMT HIGH BYTE FOR X
BPL pdl1+1 ; ALWAYS
jit3 JMP PDL.JITTER ; NOW CHECK PADDLE JITTER
*---PADDLE 1 TIMED OUT, KEEP LOOKING AT PADDLE 0
pdl0 dex
LDA $C064,X ; CHECK PADDLE 0
BPL jit5 ; TIMED OUT
INC $8 ; COUNT PDL0
NOP ; EQUALIZE TIMING
NOP
NOP
BNE pdl0+1 ; KEEP CHECKING
LDA $9
BNE jit5
INC $9
BPL pdl0+1
jit5 JMP PDL.JITTER ; NOW CHECK PADDLE JITTER
*---------------------------------
* PADDLE JITTER SMOOTHER
*
* POKE 768,<PADDLE NUMBER> 0, 1, 2, OR 3
* CALL 770
* P=PEEK(769) PADDLE VALUE 0-255
*---------------------------------
PADDLE.NUMBER .BS 1
PADDLE.VALUE .BS 1
*---------------------------------
PADDLE.JITTER
LDA PADDLE.NUMBER
AND #3 BE CERTAIN 0>=PDL#>=3
TAX
JSR MON.PREAD READ PADDLE VALUE
TYA SAVE IN A-REG TOO
CPY PADDLE.VALUE.1
BEQ .8 SAME, RETURN THIS VALUE
LDX PADDLE.VALUE.1 DETERMINE PREVIOUS DIRECTION
CPX PADDLE.VALUE.2
BCS .2 branch if INCREASING
CPY PADDLE.VALUE.1 WHAT IS CURRENT DIRECTION?
BCC .6 STILL DECREASING, SO ACCEPT IT
DEY SEE IF ONLY 1 STEP
BCS .5 ...ALWAYS
.2 CPY PADDLE.VALUE.1 DETERMINE CURRENT DIRECTION
BCS .6 STILL INCREASING, SO ACCEPT IT
INY SEE IF ONLY 1 STEP
.5 CPY PADDLE.VALUE.1 IF SAME NOW, IGNORE IT
BNE .6 USE NEW VALUE
TXA USE PREVIOUS VALUE
BCS .8 ...ALWAYS
.6 STX PADDLE.VALUE.2 OLDEST READING
STA PADDLE.VALUE.1 PREVIOUS READING
.8 STA PADDLE.VALUE CURRENT READING
RTS
*---------------------------------
PADDLE.VALUE.1 .DA #0
PADDLE.VALUE.2 .DA #0
*---------------------------------
Increased Paddle Range
LDX L32A
LDY #$00
STY L328
STY L329
LDA $C070
NOP
NOP
L311 LDA $C064,X
BPL L323
INY
BNE L311
INC L329
LDA L329
CMP #$02
BNE L311
L323 TYA
STA L328
RTS
L328: * low paddle count
L329: * high paddle count
L32A: * paddle number
The following is a 16-bit assembley routine to read both paddles at once. In 2.8 MHz mode, it works almost exactly like the // Rom code PRead called thru FWEntry, which is a nice feature. I'm not too sure of the copyright on the code, so if anyone could clarify it for me, I'd be glad to acknowledge it.
Here's the code I used. There are a few things to keep in mind with this code: Because I'm using wide registers, the X and Y values of the joystick may come back with values greater than $FF. The size of the number returned depends (in part) upon the speed of the machine that the code is running on. Accellerated IIGS's will return greater values than stock 2.8 MHz machines. Having the user do a joystick calibration at the start of the program would be one way of solving this problem.
Also, because the maximum joystick value returned is not "clipped
off" (i.e. maxed out at $FF), the X and Y maximum returned values may differ, depending on the model and wear of your joystick. And one last thing: The timing cycles of my routine are greater than the sum of the cycles required to execute the standard paddle reading routine built into the II+ ROM. But, since my routine will execute at speeds greater than 1 MHz, the net difference results in a faster joystick read. That, coupled with the fact that both paddles are read at the same time, makes this routine much faster than the standard ROM call.
; Read the joystick controls:
PRead anop
php
sei
ldal $00C070 ; trigger paddles (6)
ldy #0 ; (3)
ldx #3 ; (3)
PRtime dex ; (2)
bne PRtime ; timing loop for 1st count (2,3)
PRead2 ldal $00C064 ; check paddle 0,1 status (6)
and #$8080 ; check high bits of each (3)
beq PRXit ; exit if both are done (2)
cmp #$0080 ; see if X only is active (3)
bmi PRBoth ; > no, do both (2,3)
beq PRXOnly ; > X axis only (2,3)
PRYonly iny ; (2)
bpl PRYmore ; (just for timing) (3)
bmi PRXit ; (prevent endless loop)
PRYmore bra PRead2 ; go back for more (3)
PRXOnly inx ; (2)
bmi PRXit ; (prevent endless loop) (2)
bra PRead2 ; go back for more (3)
PRBoth inx ; (2)
iny ; (2)
nop ; match timing of others (2)
bpl PRead2 ; (3)
PRXit plp
rts ; x = pdl(0), y = pdl(1)