Hello,
i have a Joystick routine with fire and 4-direction.
But i need a 8-direction with fire button routine in assembler.
is there someone here in the forum who can help me?
BR
Sven
8-direction+fire Joystick
Moderator: Moderators
- MrSterlingBS
- Vic 20 Enthusiast
- Posts: 174
- Joined: Tue Jan 31, 2023 2:56 am
- Location: Germany,Braunschweig
Re: 8-direction+fire Joystick
I'm not sure what you mean by '8-direction': the joystick bits are independent so you can have, say, up and left, active at the same time. Could you post your code?MrSterlingBS wrote: ↑Wed Apr 12, 2023 5:06 am i have a Joystick routine with fire and 4-direction.
But i need a 8-direction with fire button routine in assembler.
is there someone here in the forum who can help me?
Re: 8-direction+fire Joystick
I remember there being such an assembler joystick-routine in the book "Programming the VIC" to be found here:
https://commodore.bombjack.org/vic-20/books-vic.htm
https://commodore.bombjack.org/vic-20/books-vic.htm
- chysn
- Vic 20 Scientist
- Posts: 1205
- Joined: Tue Oct 22, 2019 12:36 pm
- Website: http://www.beigemaze.com
- Location: Michigan, USA
- Occupation: Software Dev Manager
Re: 8-direction+fire Joystick
I do something like this. The routine returns a single bitfield containing the current directions activated.
* AND #$3C is here out of an abundance of caution, because bit 7 is used on this port. If you don't do it, it'll probably be okay.
I usually set the DDR when my game starts up and leave it set. This means that some keys don't work, which I don't care about when the joystick is the only control method. But I wrote this routine to set the DDR temporarily for maximum flexibility. Modify that depending on your needs.
After calling the routine, you just test for directions using the bit values above. For example:
In real life, you probably don't need to do a compare for every possible direction, but this should give you a jumping-off point.
Code: Select all
NORTH = $04
SOUTH = $08
WEST = $10
EAST = $80
FIRE = $20
Joystick: lda #$7f ; Set data direction register for VIA2 so that East can be read. This turns
sta $9122 ; OFF some keys, so we make sure to restore it later.
lda $9111 ; Read VIA for North (bit 2), South (bit 3), West (bit 4) and Fire (bit 5)
and #$3c ; Clear bits we don't care about (0, 1, 6, 7)*
sta $fe ; Stash this value somewhere
lda $9120 ; Read East (bit 7)
and #$80 ; We only care about bit 7 of this VIA2 port
ora $fe ; Combine bit 7 of VIA2B with previously-stashed value of $fe
pha ; Push the final result while we put the data direction register back
lda #$ff ; Put the data direction register back for VIA2
sta $9122 ; ,,
pla ; Put the register value in A
sta $fe ; And/or store it for use with BIT
rts
I usually set the DDR when my game starts up and leave it set. This means that some keys don't work, which I don't care about when the joystick is the only control method. But I wrote this routine to set the DDR temporarily for maximum flexibility. Modify that depending on your needs.
After calling the routine, you just test for directions using the bit values above. For example:
Code: Select all
jsr Joystick
cmp #(NORTH | WEST) ; Check northwest
beq move_nw
lda #FIRE
bit $fe
bne jump
VIC-20 Projects: wAx Assembler, TRBo: Turtle RescueBot, Helix Colony, Sub Med, Trolley Problem, Dungeon of Dance, ZEPTOPOLIS, MIDI KERNAL, The Archivist, Ed for Prophet-5
WIP: MIDIcast BASIC extension
he/him/his
WIP: MIDIcast BASIC extension
he/him/his
- Mike
- Herr VC
- Posts: 4842
- Joined: Wed Dec 01, 2004 1:57 pm
- Location: Munich, Germany
- Occupation: electrical engineer
Re: 8-direction+fire Joystick
As srowe already wrote, the four main directions of the joystick are independent and any combination possible within mechanical limits may appear in the VIA registers. Actually, it requires extra effort to filter out the diagonals - for example by laying out the joystick routine as C-like switch with early bail-outs/breaks, like thus:
Note the extra GOTO 7 that I added to the end of the IF statements (see the original example in the Denial Wiki). Those extra GOTOs effectively prevent the sensing of the diagonals as any further IF statements are bypassed as soon as one of the directions has been detected.
One way to effectively employ the diagonal directions is actually executing all IF statements regardless and 'collecting' all possible movements into positional changes:
That can also be combined with checks for screen limits (shown here for left direction):
For checks of collisions with fixed objects, the old position should be kept in, say, X2/Y2 and when the new X/Y-position is occupied, then restore the old X2/Y2-position into X/Y.
This is code in BASIC, however it should be straightforward to translate this to machine code.
Code: Select all
1 S=PEEK(37151):POKE37154,127:T=PEEK(37152):POKE37154,255
2 IF (128ANDT)=0 THEN PRINT "RIGHT":GOTO 7
3 IF (16ANDS)=0 THEN PRINT "LEFT":GOTO 7
4 IF (8ANDS)=0 THEN PRINT "DOWN":GOTO 7
5 IF (4ANDS)=0 THEN PRINT "UP":GOTO 7
6 IF (32ANDS)=0 THEN PRINT "FIRE"
7 [...]
One way to effectively employ the diagonal directions is actually executing all IF statements regardless and 'collecting' all possible movements into positional changes:
Code: Select all
1 S=PEEK(37151):POKE37154,127:T=PEEK(37152):POKE37154,255
2 IF (128ANDT)=0 THEN X=X+1
3 IF (16ANDS)=0 THEN X=X-1
4 IF (8ANDS)=0 THEN Y=Y+1
5 IF (4ANDS)=0 THEN Y=Y-1
6 IF (32ANDS)=0 THEN [...]
[...]
Code: Select all
[...]
3 IF (16ANDS)=0 AND X>0 THEN X=X-1
[...]
This is code in BASIC, however it should be straightforward to translate this to machine code.
- MrSterlingBS
- Vic 20 Enthusiast
- Posts: 174
- Joined: Tue Jan 31, 2023 2:56 am
- Location: Germany,Braunschweig
Re: 8-direction+fire Joystick
Dear VIC-20 friends,
Thank you for your comments, the code and the help.
Please excuse my late reaction to this and my not very precise task.
My task:
I had thought of moving a sprite over the screen with a joystick. With the query of the four cardinal points I get a good result. As an example, let a sprite (arrow) point left, right, up, and down. If no direction is pressed, a smiley appears.
But what if I control the sprite up-right, down-right, down-left or up-left? Then appears at an arrow not pointing in the direction sprite.
So how can I query the corresponding direction and display the correct sprite?
I have attached an example code.
Thank you again for your help.
Thank you for your comments, the code and the help.
Please excuse my late reaction to this and my not very precise task.
My task:
I had thought of moving a sprite over the screen with a joystick. With the query of the four cardinal points I get a good result. As an example, let a sprite (arrow) point left, right, up, and down. If no direction is pressed, a smiley appears.
But what if I control the sprite up-right, down-right, down-left or up-left? Then appears at an arrow not pointing in the direction sprite.
So how can I query the corresponding direction and display the correct sprite?
I have attached an example code.
Thank you again for your help.
Code: Select all
Sprite equ $10
z_Regs equ $20
z_HL equ z_Regs
z_L equ z_Regs
z_H equ z_Regs+1
z_BC equ z_Regs+2
z_C equ z_Regs+2
z_B equ z_Regs+3
z_DE equ z_Regs+4
z_E equ z_Regs+4
z_D equ z_Regs+5
z_As equ z_Regs+6
;Current player pos
PlayerX equ $60
PlayerY equ PlayerX+1
;Last player pos (For clearing sprite)
PlayerX2 equ PlayerX+2
PlayerY2 equ PlayerX+3
* = $1001
; BASIC program to boot the machine language code
db $0b, $10, $0a, $00, $9e, $34, $31, $30, $39, $00, $00, $00
;start of code $100A
;Screen Init
ldx #16 ;We're going to copy 16 registers
ScreenInitAgain:
dex
lda VicScreenSettings,x ;Get A parameter
sta $9000,X ;Store to the video registers at $9000
txa
bne ScreenInitAgain
lda #$1E ;Clear $1E00-$2000
sta z_h
lda #$00
sta z_l
ldx #$02 ;Clear $200 bytes
tay ;#$00
lda #9 ;Tile 9=Blank
FillZerosB:
sta (z_hl),y
dey
bne FillZerosB
inc z_h
dex
bne FillZerosB
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Define Tiles
lda #<Bitmap ;Source Bitmap Data
sta z_L
lda #>Bitmap
sta z_H
lda #<(BitmapEnd-Bitmap);Source Bitmap Data Length
sta z_C
lda #>(BitmapEnd-Bitmap)
sta z_B
lda #<$1C00 ;Tile 0 in VIC Custom Characters
sta z_E
lda #>$1C00
sta z_D
jsr DefineTiles ;Define the tile patterns
lda #3 ;Start SX
sta PlayerX
lda #3 ;Start SY
sta PlayerY
lda #0 ;Fake No Keys on first run
sta z_h
jmp StartDraw ;Force Draw of character first run
infloop:
lda #20
Raster:
cmp $9004
bne Raster
ldx PlayerX ;Back up X
ldy PlayerY ;Back up Y
lda #0
sta Sprite
jsr DrawPlayer ;Draw Player Sprite
jsr Player_ReadControlsDual
lda z_h
cmp #%11111111
beq infloop ;See if no keys are pressed
pha
StartDraw:
ldx PlayerX ;Back up X
stx PlayerX2
ldy PlayerY ;Back up Y
sty PlayerY2
jsr BlankPlayer ;Remove old player sprite
ldx PlayerX ;Back up X
ldy PlayerY ;Back up Y
pla
sta z_h
and #%00001000 ;-FLDU--R
bne JoyNotUp ;Jump if UP not pressed
dey ;Move Y Up the screen
lda #1
sta sprite
JoyNotUp:
lda z_h
and #%00010000 ;-FLDU--R
bne JoyNotDown ;Jump if DOWN not presesd
iny ;Move Y Down the screen
lda #3
sta Sprite
JoyNotDown:
lda z_h
and #%00100000 ;-FLDU--R
bne JoyNotLeft ;Move X Left
dex
lda #2
sta Sprite
JoyNotLeft:
lda z_h
and #%00000001 ;-FLDU--R
bne JoyNotRight ;Move X Right
inx
lda #4
sta Sprite
JoyNotRight:
stx PlayerX ;Update X
sty PlayerY ;Update Y
;X Boundary Check - if we go <0 we will end up back at 255
cpx #22
bcs PlayerReset
;Y Boundary Check - only need to check 1 byte
cpy #23
bcs PlayerReset
jmp PlayerPosYOk ;Not Out of bounds
PlayerReset:
ldx PlayerX2 ;Reset Xpos
stx PlayerX
ldy PlayerY2 ;Reset Ypos
sty PlayerY
PlayerPosYOk:
jsr DrawPlayer ;Draw Player Sprite
ldx #255
ldy #100
jsr PauseXY ;Wait a bit!
jmp infloop
BlankPlayer:
lda #9 ;Tile Num 9 (blank sprite)
jmp DrawSprite
DrawPlayer:
lda Sprite ;Tile Num (Smiley)
DrawSprite:
pha
stx z_b
sty z_c
jsr GetVDPScreenPos ;Calculate Tilemap mempos
pla
sta (z_hl),y ;Transfer Tile to ram
lda z_h
clc
adc #$78 ;add Offset to Color Ram ($9600)
sta z_h
lda #4 ;Color
sta (z_hl),y ;Set Tile Color
rts
Bitmap: ;Smiley Sprite 0
DB %00111100 ; 0
DB %01111110 ; 1
DB %11011011 ; 2
DB %11111111 ; 3
DB %11111111 ; 4
DB %11011011 ; 5
DB %01100110 ; 6
DB %00111100 ; 7
; arrow up Sprite 1
db $00,$18,$3C,$7E,$18,$18,$18,$18
; arrow left Sprite 2
db $00,$10,$30,$7F,$7F,$30,$10,$00
; arrow down Sprite 3
db $18,$18,$18,$18,$7E,$3C,$18,$00
; arrow right Sprite 4
db $00,$08,$0C,$FE,$FE,$0C,$08,$00
; arrow right-up Sprite 5
db $00,$1E,$0E,$1E,$3A,$70,$20,$00
; arrow left-up Sprite 6
db $00,$78,$70,$78,$5C,$0E,$04,$00
; arrow left-down Sprite 7
db $00,$04,$0E,$5C,$78,$70,$78,$00
; arrow right-down Sprite 8
db $00,$20,$70,$3A,$1E,$0E,$1E,$00
DS 8,0 ;Blank Sprite 9
BitmapEnd:
Player_ReadControlsDual:
lda #%01111111
sta $9122 ;Set Data Direction of port B to READ (0=read)
; lda #%11000011
; sta $9113 ;Set Data Direction of port A to READ (0=read)
lda $9120 ;Port B (R------- Switch)
sta z_as
lda $911F ;Port A (--FLDU-- Switches)
rol z_as
rol
sta z_h ;-FLDU--R
; lda #255
; sta $9122 ;Reset port B (for Keyb col scan)
rts
PauseXY:
dex
bne PauseXY
dey
bne PauseXY
rts
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Address= $1E00 + (Ypos * 22) + Xpos
GetVDPScreenPos: ; BC=XYpos
lda #$1e ;Screen base is $1E00
sta z_h ;Colors at $9600 (add $7800 offset)
lda z_b ;Xpos
sta z_l
ldy z_c ;Ypos
beq GetVDPScreenPos_YZero
GetVDPScreenPos_Addagain: ;Repeatedly add screen width (22) Y times
clc
lda z_l
adc #22 ;22 bytes per line
sta z_l
lda z_h
adc #0 ;Add Carry
sta z_h
dey
bne GetVDPScreenPos_Addagain
GetVDPScreenPos_YZero:
rts
;Copy Data to the Character Defs from ram/rom to char ram
DefineTiles:
ldy #0
DefineTiles2:
lda (z_HL),Y ;Copy From z_HL
sta (z_DE),Y ;To z_ DE
iny ;Inc Byte
BNE DefineTiles_SkipInc1
INC z_H ;Inc H bytes
INC z_D
DefineTiles_SkipInc1:
DEC z_C ;Dec Counter
BNE DefineTiles2
LDA z_B
BEQ DefineTiles_Done
DEC z_B
jmp DefineTiles2
DefineTiles_Done:
rts
;LLLL options
; 0000 ROM 8000 32768
; 0001 8400 33792
; 0010 8800 34816
; 0011 8C00 35840
; 1000 RAM 0000 0000
; 1001 xxxx
; 1010 xxxx unavail.
; 1011 xxxx
; 1100 1000 4096
; 1101 1400 5120
; 1110 1800 6144
; 1111 1C00 7168 <---
VicScreenSettings:
db $0C ;$9000 - horizontal centering
db $26 ;$9001 - vertical centering
db $96 ;$9002 - set # of columns /
;Bit7 = screen base bit ($16 for screen at $1000)
db $AE ;$9003 - set # of rows
db $7A ;$9004 - TV raster beam line
db $FF ;$9005 - bits 0-3 start of character memory /
;bits 4-7 is rest of video address
;$(CF for screen at $1000)
db $57 ;$9006 - horizontal position of light pen
db $EA ;$9007 - vertical position of light pen
db $FF ;$9008 - Digitized value of paddle X
db $FF ;$9009 - Digitized value of paddle Y
db $00 ;$900A - Frequency for oscillator 1 (low)
db $00 ;$900B - Frequency for oscillator 2 (medium)
db $00 ;$900C - Frequency for oscillator 3 (high)
db $00 ;$900D - Frequency of noise source
db $00 ;$900E - bit 0-3 sets volume of all sound /
;bits 4-7 are auxiliary color information
db $00+13 ;$900F - Screen and border color register
- chysn
- Vic 20 Scientist
- Posts: 1205
- Joined: Tue Oct 22, 2019 12:36 pm
- Website: http://www.beigemaze.com
- Location: Michigan, USA
- Occupation: Software Dev Manager
Re: 8-direction+fire Joystick
The main problem with the above is that you've got four comparisons to a cardinal direction, each falling through to the next, and the sprite used is based on the last match. Rather than doing a bunch of brute-force comparisons, consider a data table containing character codes indexed by the values of a bitfield. Your table will have 16 rows in it (2^4), but you can mark (not skip) conditions that aren't designed to happen (north/south).
Your bitfield could be constructed using the Joystick routine I provided above. To refresh memories, its results are
You can massage this a bit to construct a more compact table index:
And then the data table would look like this. You could simply leave out everything after SOUTHEAST to save a few bytes, since you won't get those results with a regular joystick.
You can extend this concept to behavior as well as visual presentation. Anything that you can express as data, you can put in a lookup table, so that you don't have to write a bunch of "what if it's this?" code.
And just for some extra info, AND sets the zero flag when no bits match, so keep that in mind for post-AND branching. To be specific, you want it to be
Your bitfield could be constructed using the Joystick routine I provided above. To refresh memories, its results are
Code: Select all
NORTH = $04
SOUTH = $08
WEST = $10
EAST = $80
FIRE = $20
Code: Select all
jsr Joystick ; Get joystick bitfield as above
and EAST ; We want to move East over into bit 5
lsr ; ,,
lsr ; ,,
ora $fe ; Recombine it with North, South, and West
lsr ; Shift everything so that bit 0=north, 1=south, 2=west, 3=east
lsr ; ,,
and #$0f ; Restrict range
tay ; Y is now the index to the sprite character code table
lda SprTable,y ; A is the looked-up sprite number
etc...
Code: Select all
SprTable: .byte SMILEY ; 0000
.byte NORTH ; 0001
.byte SOUTH ; 0010
.byte INVALID ; 0011 north/south doesn't happen
.byte WEST ; 0100
.byte NORTHWEST ; 0101
.byte SOUTHWEST ; 0110
.byte INVALID ; 0111 north/south/west doesn't happen
.byte EAST ; 1000
.byte NORTHEAST ; 1001
.byte SOUTHEAST ; 1010
.byte INVALID ; 1011 north/south/east doesn't happen
.byte INVALID ; 1100 east/west doesn't happen
.byte INVALID ; 1101 north/east/west doesn't happen
.byte INVALID ; 1110 south/east/west doesn't happen
.byte INVALID ; 1111 north/south/east/west doesn't happen
And just for some extra info, AND sets the zero flag when no bits match, so keep that in mind for post-AND branching. To be specific, you want it to be
Code: Select all
and #%00001000 ;-FLDU--R
BEQ JoyNotUp ;Jump if UP not pressed
VIC-20 Projects: wAx Assembler, TRBo: Turtle RescueBot, Helix Colony, Sub Med, Trolley Problem, Dungeon of Dance, ZEPTOPOLIS, MIDI KERNAL, The Archivist, Ed for Prophet-5
WIP: MIDIcast BASIC extension
he/him/his
WIP: MIDIcast BASIC extension
he/him/his