Fusion-c : fastest routine to read joystick port

By ericb59

Paladin (875)

ericb59's picture

20-11-2019, 20:55

Hello,

Actual ASM routine included in Fusion-c to read joystick is this one :

push ix
  ld   ix,#0
  add  ix,sp
  ld   a,4(ix)
  push iy
  ld   ix,#0x00d5       ; Bios Joystick Read routine
  ld   iy,(0xFCC0)      ; mainrom slotaddress
  call 0x001c           ; interslotcall
  ei
  ld   l,a
  pop  iy
  pop  ix
  ret

I want something fastest...

In your specialist opinion, which solution is the best, and the fastest one ?

 di
  ld  a, #15           
  out (#0xA0), a        
  in  a, (#0xA2)        ; Read register 15
  and #0x8f
  out (#0xA1), a

  ld  a, #14
  out (#0xA0), a
  ei
  in  a, (#0xA2)      ;read left right up down and button 1 and 2 from register 14
  cpl                 ; invert the bits , thus 1 means pressed

  ld (hl),#0          ; Up reset and test
  bit 0,a
  call nz,Set
  inc hl

  ld (hl),#0         ; down reset and test
  bit 1,a
  call nz,Set
  inc hl

  ld (hl),#0        ; left reset and test
  bit 2,a
  call nz,Set
  inc hl

  ld (hl),#0        ; Right reset and test
  bit 3,a
  call nz,Set
  inc hl

  ld (hl),#0        ; Button1 reset and test
  bit 4,a
  call nz,Set
  inc hl

  ld (hl),#0        ; Button2 reset and test
  bit 5,a 
  call nz,Set
  inc hl

  and #0b00001111    ; send global direction as byte value
  ld (hl),a
  ret

  Set:            ; set to 1 if direction pressed
    ld (hl),#1
  ret

or this one ?

 di
  ld  a, #15
  out (#0xA0), a
  in  a, (#0xA2)        ; Read register 15
  and #0x8f
  out (#0xA1), a

  ld  a, #14
  out (#0xA0), a
  ei
  in  a, (#0xA2)      ;read left right up down and button 1 and 2 from register 14
  cpl                 ; invert the bits , thus 1 means pressed
  ld c,a

  and #0b00001111    ; send global direction as byte value
  ld (hl),a

  ld b,#6
bcl:
  and #1
  inc hl
  ld (hl),a
  srl c
  ld a,c
  djnz bcl
  ret

Edit : The new routine will send data to this C structure:

typedef struct {
    unsigned char up;
    unsigned char down;
    unsigned char left;
    unsigned char right;
    unsigned char button1;
    unsigned char button2;
    unsigned char direction;
} JOY_DATA;
Login or register to post comments

By Danjovic

Expert (83)

Danjovic's picture

21-11-2019, 09:23

fastest way is to return raw data and use macros on the C code to test bits more comfortably.

...#define up() (rawData & (1<<0)==0)
...
rawData = readPort(A);
if (up()) { // do stuff
...}
...

that will let you test the "unexpected" up+down / left+right and use them as "select" and "pause"

By gdx

Prophet (3083)

gdx's picture

21-11-2019, 09:31

You must preserve the bit status of Code/Kana key LED when you handle the joystick.
And the ideal would also be to update at same time the system variable TRGFLG (0F3E8h).

; Input: A = joystick port (1 or 2)
; Output: A = Bits 0-5 for UP, Down, Left, Right, B1 and B2

	and	2
	ld	a,00001111b	; Joystick 1
	jr	z,Joy2		; Jump if A was 1
	ld	a,01001111b	; Joystick 2
Joy2:
	ld	b,a
	ld	a,15
	out	(#0xA0),a
	in	a,(#0xA2)
	and	080h		; Keep the bit 7
	or	b
	out	(#0xA1),a

	ld	a,14
	out	(#0xA0),a
	in	a,(#0xA2)

I think it's better to do a routine that uses BIOS and another with quick access.

By DarkSchneider

Paladin (880)

DarkSchneider's picture

21-11-2019, 11:48

Notice that reading PSG registers directly is not allowed on MSX standard. You have to use the BIOS. The main problem is the inter-slot call, then you simply could create a function to use at VBLANK to use during interruption, with the BIOS set at page 0, so calling the "Bios Joystick Read routine" directly.

By gdx

Prophet (3083)

gdx's picture

21-11-2019, 13:48

Bit 7-6 of the register 7 should control PSG I/O ports A and B but it's fixed on MSX, We must not change them.

Bit 7 (port A direction control) = must be always reset and bit 6 (port B direction control) set.

So the register 15 (value on port B) is an output but bit 7 content can still be read. Register 14 (value from port A) is fixed on input only so only the received signals can be read.

By NYYRIKKI

Enlighted (5396)

NYYRIKKI's picture

21-11-2019, 16:05

This looks pretty bad:

Quote:
  ld (hl),#0          ; Up reset and test
  bit 0,a
  call nz,Set
  inc hl

  ld (hl),#0         ; down reset and test
  bit 1,a
  call nz,Set
  inc hl

  ld (hl),#0        ; left reset and test
  bit 2,a
  call nz,Set
  inc hl

  ld (hl),#0        ; Right reset and test
  bit 3,a
  call nz,Set
  inc hl

  ld (hl),#0        ; Button1 reset and test
  bit 4,a
  call nz,Set
  inc hl

  ld (hl),#0        ; Button2 reset and test
  bit 5,a 
  call nz,Set
  inc hl

  and #0b00001111    ; send global direction as byte value
  ld (hl),a
  ret

How about something like:

	ld c,a
	and 15
	ld (hl),a ; "global direction as byte value"
	inc hl

	xor a
	rr c
	adc a,a
	ld (hl),a ; Up
	inc hl

	xor a
	rr c
	adc a,a
	ld (hl),a ; Down
	inc hl

	xor a
	rr c
	adc a,a
	ld (hl),a ; Left
	inc hl

	xor a
	rr c
	adc a,a
	ld (hl),a ; Right
	inc hl

	xor a
	rr c
	adc a,a
	ld (hl),a ; B1
	inc hl

	xor a
	rr c
	adc a,a
	ld (hl),a ; B2
	ret

By sd_snatcher

Prophet (3092)

sd_snatcher's picture

21-11-2019, 16:46

The joystick ports should be read at most once per frame, at the frame interrupt. It’s an unnecessary waste of CPU to read it more than that, since there would be no extra video frames to represent such state changes. It’s the best optimization anyone can do.

<shameless advertising>
That said, why don’t you save your precious time and, instead of reinventing the wheel, place some wrappers to use HIDlib? ;)

It has very efficient algorithms, and a plethora of different joysticks/devices would be supported by Fusion-C with very little effort.

And it passes the MSX AcidTests too, so full compatibility is assured.
</shameless advertising>

By ericb59

Paladin (875)

ericb59's picture

21-11-2019, 18:09

@NYYRIKKI : "Looks Pretty bad", ok I thrust you, but why ? Please explain. I need to learn !

@sd_snatcher : You are right, I do not want to reinvent the wheel. The fact is I do not know all existing files, libraries, software... existing for MSX Wink
I Will check that.

Thanks to All.

By gdx

Prophet (3083)

gdx's picture

22-11-2019, 08:47

I think that should work:

	push	ix
	ld	ix,#0
	add	ix,sp
	ld	a,4(ix)

; Input: A = joystick port (1 or 2)

	and	#2
	ld	c,0b00001111	; Joystick 1
	jr	z,Joy2		; Jump if A was 1
	ld	c,0b01001111	; Joystick 2
Joy2:
	ld	a, #15
	out	(#0xA0),a
	in	a,(#0xA2)
	and	#0x80		; Keep the bit 7 (KANA/Code LED)
	or	c
	out	(#0xA1),a

	ld	a, #14
	out	(#0xA0),a
	in	a,(#0xA2)
	ld c,a

	xor a
	rrc c
	rla
	ld (hl),a ; Up
	inc hl

	xor a
	rrc c
	rla
	ld (hl),a ; Down
	inc hl

	xor a
	rrc c
	rla
	ld (hl),a ; Left
	inc hl

	xor a
	rrc c
	rla
	ld (hl),a ; Right
	inc hl

	xor a
	rrc c
	rla
	ld (hl),a ; B1
	inc hl

	xor a
	rrc c
	rla
	ld (hl),a ; B2

	ld a,c
	rrca
	rrca
	and #15
	inc hl
	ld (hl),a ; "global direction as byte value"

	pop	ix
	ret

I think line "and #15" is useless.
You can handle the byte of global direction in first like Nyyrikki to gain a bit of time. ("ld a,c" and the both rrca become useless in this case)

By akumajo

Resident (37)

akumajo's picture

22-11-2019, 10:19

I wonder about the usefulness of testing each value in the function, the fastest way would be to return only the raw value and then test the bit(s) that interests us in the program after calling the function. This to avoid to test once in the function and once in the program.

Looking at all the answers, it's not easy to find what suits all programmers!

By gdx

Prophet (3083)

gdx's picture

22-11-2019, 10:36

It would be nice to add a routine for JoyMega and JoySNES.

akumajo wrote:

I wonder about the usefulness of testing each value in the function, the fastest way would be to return only the raw value and then test the bit(s) that interests us in the program after calling the function.

I agree.