Joystick assembler routine
Moderator: Moderators
Joystick assembler routine
Hello everybody, I'm trying to get into VIC-20 assembly programming and wanted to find a joystick routine.
It's surprisingly diffcult to find one!
Eventually I did find one on here:
joy_dir = $F7
joy1 = $911F
joy2 = $9120
LDA #$7F
STA $9122 ;set DDR for input
LDA joy1
AND #$3C
STA joy_dir
LDA joy2
AND #$80
ORA joy_dir
STA joy_dir
LDA #$FF
STA $9122
RTS
I assumed in practise this works like this:
joy_dir = $F7
joy1 = $911F
joy2 = $9120
LDA #$7F
STA $9122 ;set DDR for input
get LDA joy1
AND #$3C
STA joy_dir
LDA joy2
AND #$80
ORA joy_dir
STA joy_dir
JMP get
LDA #$FF
STA $9122
RTS
I didn't have any info on which values the joy_dir register would take when you move the joystick, so I wrote a little program which I thought would read the values.
I came up with: no input #188, left #172, right #60, up #184, down #180. Is that correct so far?
I used the above routine in my program like this:
get LDA joy1
AND #$3C
STA joy_dir
LDA joy2
AND #$80
ORA joy_dir
STA joy_dir
CMP #188
BEQ get
CMP #180 ; Joystick up?
BNE *+5
JMP up
CMP #172 ; Joystick left?
BNE *+5
JMP left
JMP get
Trouble is, it doesn't work as expected.
It's to move a little character across the screen, but by pressing the joystick left one, it's moving it over not just once, but as if the joystick is pressed to the left all the time.
Worse still, when pressing the joystick up, nothing happens at all.
Could someone tell me what I might be doing wrong? Thank you!
It's surprisingly diffcult to find one!
Eventually I did find one on here:
joy_dir = $F7
joy1 = $911F
joy2 = $9120
LDA #$7F
STA $9122 ;set DDR for input
LDA joy1
AND #$3C
STA joy_dir
LDA joy2
AND #$80
ORA joy_dir
STA joy_dir
LDA #$FF
STA $9122
RTS
I assumed in practise this works like this:
joy_dir = $F7
joy1 = $911F
joy2 = $9120
LDA #$7F
STA $9122 ;set DDR for input
get LDA joy1
AND #$3C
STA joy_dir
LDA joy2
AND #$80
ORA joy_dir
STA joy_dir
JMP get
LDA #$FF
STA $9122
RTS
I didn't have any info on which values the joy_dir register would take when you move the joystick, so I wrote a little program which I thought would read the values.
I came up with: no input #188, left #172, right #60, up #184, down #180. Is that correct so far?
I used the above routine in my program like this:
get LDA joy1
AND #$3C
STA joy_dir
LDA joy2
AND #$80
ORA joy_dir
STA joy_dir
CMP #188
BEQ get
CMP #180 ; Joystick up?
BNE *+5
JMP up
CMP #172 ; Joystick left?
BNE *+5
JMP left
JMP get
Trouble is, it doesn't work as expected.
It's to move a little character across the screen, but by pressing the joystick left one, it's moving it over not just once, but as if the joystick is pressed to the left all the time.
Worse still, when pressing the joystick up, nothing happens at all.
Could someone tell me what I might be doing wrong? Thank you!
Re: Joystick assembler routine
I've not had time to try your code out but I've had a look at my FORTH implementation to refresh my memory.naujoks wrote: I didn't have any info on which values the joy_dir register would take when you move the joystick, so I wrote a little program which I thought would read the values.
I came up with: no input #188, left #172, right #60, up #184, down #180. Is that correct so far?
Remember each direction is a bit in a VIA register
Code: Select all
VIA1PA2 = $911F ; VIA 1 DRA, no handshake
; bit function
; --- --------
; 7 serial ATN out
; 6 cassette switch
; 5 joystick fire, light pen
; 4 joystick left, paddle X fire
; 3 joystick down
; 2 joystick up
; 1 serial DATA in
; 0 serial CLK in
VIA2PB = $9120 ; VIA 2 DRB, keyboard column
; bit function
; --- --------
; 7 joystick right, paddle Y fire
; 3 cassette write line
Re: Joystick assembler routine
Welcome to try my routine....
Use like this...
jsr ReadPhysicalJoystick
;joystickFireButtonLatch - non zero - if fire pressed since last cleared
lda lastJoystickRead
and #JOY_PORT_LEFT
bne @left
lda lastJoystickRead
and #JOY_PORT_RIGHT
bne @right
lda lastJoystickRead
and #JOY_PORT_UP
bne @up
lda lastJoystickRead
and #JOY_PORT_DOWN
bne @down
; else---
jmp @none
@left: jmp HandleLeft
@right: jmp HandleRight
@up: jmp HandleUp
@down: jmp HandleDown
@none: jmp Nothing
Use like this...
jsr ReadPhysicalJoystick
;joystickFireButtonLatch - non zero - if fire pressed since last cleared
lda lastJoystickRead
and #JOY_PORT_LEFT
bne @left
lda lastJoystickRead
and #JOY_PORT_RIGHT
bne @right
lda lastJoystickRead
and #JOY_PORT_UP
bne @up
lda lastJoystickRead
and #JOY_PORT_DOWN
bne @down
; else---
jmp @none
@left: jmp HandleLeft
@right: jmp HandleRight
@up: jmp HandleUp
@down: jmp HandleDown
@none: jmp Nothing
Code: Select all
;*********************************************************************
; JOYSTICK
;
; Joystick Ports
JOY_PORT_DDR = 37139
JOY_PORT = 37137
JOY_PORT_SW3_DDR = 37154
JOY_PORT_SW3 = 37152
; Joystick Switches
JOY_PORT_UP = %00000100
JOY_PORT_DOWN = %00001000
JOY_PORT_LEFT = %00010000
JOY_PORT_FIRE = %00100000
JOY_PORT_RIGHT = %10000000
JOY_PORT_READ_MASK = JOY_PORT_UP|JOY_PORT_DOWN|JOY_PORT_LEFT|JOY_PORT_FIRE
JOY_PORT_READ_MASK_SW3 = JOY_PORT_RIGHT
; stored in lastJoystickRead
JOY_PORT_MOVING_MSK = JOY_PORT_UP|JOY_PORT_DOWN|JOY_PORT_LEFT|JOY_PORT_RIGHT
;------------------------------ imports -----------------------------
; ------------------------------ exports -----------------------------
.global ReadPhysicalJoystick
.global lastJoystickRead
.global joystickFireButtonLatch
.global WaitForFire
; ------------------------------ Vars -----------------------------
forceFirebuttonRelease: .byte 0
lastJoystickRead: .byte 0
joystickFireButtonLatch: .byte 0
; ------------------------------ WaitForFire -----------------------------
;-- Blocking wait for fire button
WaitForFire:
lda #0
sta joystickFireButtonLatch
@loop:
jsr ReadPhysicalJoystick
lda joystickFireButtonLatch
beq @loop
rts
; ------------------------------ ReadPlayerJoystick -----------------------------
;-- Reads joystick and leaves a result in the global lastJoystickRead and Accumulator
ReadPhysicalJoystick:
lda JOY_PORT_DDR
pha
lda JOY_PORT_SW3_DDR ; will mess up keyboard if not restored...
pha
lda #0
sta JOY_PORT_DDR
sta JOY_PORT_SW3_DDR
lda JOY_PORT
eor #$FF ; convert closed to be represented as '1'
and #JOY_PORT_READ_MASK
sta lastJoystickRead
lda JOY_PORT_SW3
eor #$FF
and #JOY_PORT_READ_MASK_SW3
ora lastJoystickRead
sta lastJoystickRead
; see if we need to let go
lda forceFirebuttonRelease
beq @normal
; reaquire actual joystick value
lda lastJoystickRead
and #JOY_PORT_FIRE
bne @notReleased
@released:
; ignore the fire button as not yet let go
lda #0
sta forceFirebuttonRelease
lda lastJoystickRead
jmp @normal
@notReleased:
; ignore the fire button as not yet let go
lda lastJoystickRead
and #<(~JOY_PORT_FIRE)
sta lastJoystickRead
jmp @normal
@normal:
; use to allow latching of the fire button with more frequent calling of this
; routine..
lda lastJoystickRead
and #JOY_PORT_FIRE
ora joystickFireButtonLatch
sta joystickFireButtonLatch
pla
sta JOY_PORT_SW3_DDR ; will mess up keyboard if not restored...
pla
sta JOY_PORT_DDR
; return with current value in accumulator
lda lastJoystickRead
rts
Re: Joystick assembler routine
Thank you for your help!
The FORTH implementation doesn't seem to offer me any help, unfortunately.
Beamrider, your routine looks good, but there are bits in there which I and CBM prg Studio don't understand, subsequently it throws some errors.
Would you be kind enough to let me know what various lines do?
1.
JOY_PORT_READ_MASK = JOY_PORT_UP|JOY_PORT_DOWN|JOY_PORT_LEFT|JOY_PORT_FIRE (this is something that I'm not familiar with, though CBM prg Studio doesn't flag it)
2.
; ------------------------------ exports -----------------------------
.global ReadPhysicalJoystick
.global lastJoystickRead
.global joystickFireButtonLatch
.global WaitForFire
This block gets marked as "Invalid instruction or Macro" in the first line, and then subsequently duplicate label.
It's probably just a question of semantics and your assembler does it differently from CBM prg Studio. Perhaps you know how CBM prg Studio handles this? Otherwise if you let me know what kind of function this is, I can try and find out the equivalent myself.
Thank you!!!!
The FORTH implementation doesn't seem to offer me any help, unfortunately.
Beamrider, your routine looks good, but there are bits in there which I and CBM prg Studio don't understand, subsequently it throws some errors.
Would you be kind enough to let me know what various lines do?
1.
JOY_PORT_READ_MASK = JOY_PORT_UP|JOY_PORT_DOWN|JOY_PORT_LEFT|JOY_PORT_FIRE (this is something that I'm not familiar with, though CBM prg Studio doesn't flag it)
2.
; ------------------------------ exports -----------------------------
.global ReadPhysicalJoystick
.global lastJoystickRead
.global joystickFireButtonLatch
.global WaitForFire
This block gets marked as "Invalid instruction or Macro" in the first line, and then subsequently duplicate label.
It's probably just a question of semantics and your assembler does it differently from CBM prg Studio. Perhaps you know how CBM prg Studio handles this? Otherwise if you let me know what kind of function this is, I can try and find out the equivalent myself.
Thank you!!!!
Re: Joystick assembler routine
PS: It might be easier if you tell me which crossassembler you're using, and I can check it out myself.
Re: Joystick assembler routine
I use CA65
You can probably ignore the global directives and the other lines you queried are defining constants that's all.
You can probably ignore the global directives and the other lines you queried are defining constants that's all.
Re: Joystick assembler routine
Thanks, I'll try to work through it.
Just out of interest, if this line
JOY_PORT_READ_MASK = JOY_PORT_UP|JOY_PORT_DOWN|JOY_PORT_LEFT|JOY_PORT_FIRE
defines a variable, what does it actually define?
Just out of interest, if this line
JOY_PORT_READ_MASK = JOY_PORT_UP|JOY_PORT_DOWN|JOY_PORT_LEFT|JOY_PORT_FIRE
defines a variable, what does it actually define?
Re: Joystick assembler routine
I see, so do you think
JOY_PORT_READ_MASK = JOY_PORT_UP OR JOY_PORT_DOWN OR JOY_PORT_LEFT OR JOY_PORT_FIRE
would do the same thing in CBM Studio?
It also doesn't like this line:
and #<(~JOY_PORT_FIRE)
JOY_PORT_READ_MASK = JOY_PORT_UP OR JOY_PORT_DOWN OR JOY_PORT_LEFT OR JOY_PORT_FIRE
would do the same thing in CBM Studio?
It also doesn't like this line:
and #<(~JOY_PORT_FIRE)
Re: Joystick assembler routine
not sure - depends if the OR is a logical or bitwise operator. You should be able to use + in this instance instead.
the other line just takes the lower 8 bits - I'm not sure why it's there without revisiting the code Try removing the '<'. In any case the value would be ~%00100000 = %11011111
btw, these are the kind of niggles that made me switch away from CbmPrg
the other line just takes the lower 8 bits - I'm not sure why it's there without revisiting the code Try removing the '<'. In any case the value would be ~%00100000 = %11011111
btw, these are the kind of niggles that made me switch away from CbmPrg
Re: Joystick assembler routine
I can't seem to find the CA65 to download anywhere, would be so kind an point me in the right direction?
Re: Joystick assembler routine
I've found a routine which I've disassembled like this:
LDA $911F
EOR #$FF
AND #$3C
LDX #$7F
SEI
STX $9122
LDY $9120
BMI jump
ORA #$02
jump LDX #$FF
STX $9122
CLI
LSR A
STA $90
$90 holds direction:
up: 2
down: 4
left: 8
right: 1
fire: 16
down-left: 12
up-left: 10
up-right: 3
down-right: 5
It seems to work so far. One thing it does is move my little character right across the screen, even when it press the joystick very briefly. It seems read even that short press as pressing it hundreds of time. As my program grows, I expect the natural slow down prevent it from interpreting the one press of the joystick as a holding down, but it would be interesting to find out if there's a way of clearing the input after a very shot amount of time.
LDA $911F
EOR #$FF
AND #$3C
LDX #$7F
SEI
STX $9122
LDY $9120
BMI jump
ORA #$02
jump LDX #$FF
STX $9122
CLI
LSR A
STA $90
$90 holds direction:
up: 2
down: 4
left: 8
right: 1
fire: 16
down-left: 12
up-left: 10
up-right: 3
down-right: 5
It seems to work so far. One thing it does is move my little character right across the screen, even when it press the joystick very briefly. It seems read even that short press as pressing it hundreds of time. As my program grows, I expect the natural slow down prevent it from interpreting the one press of the joystick as a holding down, but it would be interesting to find out if there's a way of clearing the input after a very shot amount of time.
Re: Joystick assembler routine
You probably want to 'de-bounce' by storing the value from the previous call and only acting when the value is different in a later call.
Code: Select all
LDA #0
loop STA prev
JSR joy
LDA $90
CMP prev
BNE move
JMP loop
Re: Joystick assembler routine
That first one routine looks like it was written by me a while ago. It should work correctly but I haven't played around with the Vic 20 for a few years now. The routine in it's entirety looked like this:
Code: Select all
joy_dir = $f7 ;could be any location in RAM
joy1 = $911F
joy2 = $9120
joy_moves
JSR joy_stick ;check joystick
LDA joy_dir
CMP #$b8 ; up
BEQ chk_up
CMP #$ac ; left
BEQ chk_left
CMP #$b4 ; down
BEQ chk_down
CMP #$3c ; right
BEQ chk_right
JMP joy_moves
joy_stick
LDA #$7f ;
STA $9122 ;set DDR for input
LDA joy1
AND #$3c
STA joy_dir
LDA joy2
AND #$80
ORA joy_dir
STA joy_dir
LDA #$ff
STA $9122
RTS
Directions Are As Follows:
Dec Hex Dec Hex
=========================================================
No Movement = 188($bc) Button Pressed = 156($9c)
UP = 184($b8) Up = 152($98)
Up/Right = 56 ($38) Up/Right = 24 ($18)
Right = 60 ($3c) Right = 28 ($1c)
Down/Right = 52 ($34) Down/Right = 20 ($14)
Down = 180($b4) Down = 148($94)
Down/Left = 164 Down/Left = ???
Left = 172 left = 140
Up/Left = 168 Up/Left = 136
Rob