Difference between revisions of "Reading Controllers"

From veswiki
Jump to: navigation, search
m (1 revision)
(Reading the four console buttons)
 
(14 intermediate revisions by the same user not shown)
Line 3: Line 3:
  
 
The simplest use of hand controller is to just pause and wait for a movement, this subroutine does just that, you call it with:
 
The simplest use of hand controller is to just pause and wait for a movement, this subroutine does just that, you call it with:
 
pi wait.4.controller.input
 
 
 
<pre>
 
<pre>
; program
+
; Add this subroutine call to your code:
 
 
; wait for controller movement
 
 
 
pi wait.4.controller.input
 
 
 
 
 
; either hand control has been moved in some direction
 
 
 
; program continues....
 
 
 
  
 +
pi wait.4.controller.input
  
 +
; Movement from either Hand Controller will continue the program
  
  
 +
; here's the actual subroutine, copy and paste to your program where convenient
  
 
wait.4.controller.input:
 
wait.4.controller.input:
 
; see if one of the hand controllers has moved
 
; see if one of the hand controllers has moved
clr
+
clr ; clear accumulator
outs 0
+
outs 0 ; enable input from both hand controllers
outs 1 ; check right hand controller
+
outs 1 ; clear latch of port of right hand controller
ins 1
+
outs 4 ; clear latch of port of left hand controller
com ; re-invert controller data
+
ins 1 ; fetch inverted data from right hand controller
bnz wait.4.controller.input.end ; if no movement then input is 0 -> no branch
+
com ; invert controller data (a %1 now means active)
 +
bnz wait.4.controller.input.end ; if no movement then input is 0 -> no branch
 
; check the other controller
 
; check the other controller
clr
+
ins 4 ; fetch inverted data from left hand controller
outs 4 ; check left hand controller
+
com ; invert controller data (if bit is 1 it means active)
ins 4
+
bz wait.4.controller.input ; if there's no indata repeat
com
 
bnz wait.4.controller.input.end
 
br wait.4.controller.input ; re-test until we have some movement
 
  
 
wait.4.controller.input.end:
 
wait.4.controller.input.end:
 +
pop ; return from subroutine, controller data in A
 +
</pre>
  
pop</pre>
+
== Controller directions in the register ==
  
 +
After the hand controller data has been read from the port latch a 1 means inactive and 0 means active.
 +
It's common to want the 1 showing active signal therefore it's inverted with the com opcode as seen above.
 +
This is not a necessary step, port data could just as easily be used and save a machine cycle.
  
 
+
The 8 bits are stored in Accumulator (A) and the bits mean this:
== Controller directions in the register ==
 
 
 
After the hand controller data has been read it needs to be inverted since it is inverted when read - we do that to get the real data back.
 
The 8 bits is stored in Ackumulator (A) and the bits mean this:
 
  
 
<pre>
 
<pre>
Line 62: Line 51:
  
  
</pre>
 
 
So if we get the value %10000000 that means the hand controller read is being pushed down.
 
 
<pre>
 
 
Port-data
 
Port-data
 
%00000001  right
 
%00000001  right
Line 77: Line 61:
 
%10000000  push down
 
%10000000  push down
  
Combinations are of course possible to:
+
Combinations are possible to:
 
 
 
%10000010  push down + left
 
%10000010  push down + left
  
And there are combinations that are impossible, since they are opposite directions:
+
There are combinations that are impossible with a normal fully functional controller since they are opposite directions:
 
 
 
%11000000  push down + pull up
 
%11000000  push down + pull up
  
 +
The "Jet Stick" however could do this as the fire button is parallel to the push down function.
 
</pre>
 
</pre>
  
After reading the controller/s you can store the result in a register to use later or check directly which direction was chosen. Let's suppose you have a game where you can move forward, backward, right or left with the right hand controller. If we store the result of the controller in let say register 8, this is how to do it:
+
After reading the controller/s it's handy to store it in a register (or in available RAM) for use later <br>
 
+
unless checking directly which direction was chosen. <br>
<pre></pre>
+
Imagine you have written a game where you can move forward, backward, right or left with the right hand controller. <br>
 
+
If we store the result of the controller in register 8, this is how to do it:
  
 
== Store result for later use ==
 
== Store result for later use ==
Line 122: Line 105:
  
  
We now have one of these bitpatterns in r8:
+
We now have one of these bit patterns in r8:
  
 
%00000000 no movement
 
%00000000 no movement
Line 134: Line 117:
 
%00001010 forward + left
 
%00001010 forward + left
  
Nothing else is possible unless something is broken.
+
Nothing else is possible with your normal controller unless something is broken.
 +
 
 +
The controller value can then be compared to the values above to decide what to do next, move the player perhaps.
 +
 
 +
</pre>
 +
 
 +
== Reading the four console buttons ==
 +
 
 +
There's a routine in the Channel F firmware that can be used directly if needed.<br>
 +
It starts at $00C1, stores the result in register 4, register 5 and 6 are also used.
 +
 
 +
Port 0 needs to be cleared at least once in the code before reading buttons:
 +
<pre>
 +
                clr
 +
                outs 0
 
</pre>
 
</pre>
  
 +
<pre>
 +
readbuts:      ins  0                    ; $00C1 - read buttons
 +
                com                      ; Invert port data, 1 now means pushed
 +
                ni  $0F                  ; Just keep button data
 +
                bz  readbuts            ;
 +
                lr  $4, A                ; Button indata now in r4
 +
 +
                li  $FF                  ; Set r5 to $FF and decrease it
 +
                lr  5, A                ;
 +
debounce:      ds  5                    ;
 +
                bnz  debounce            ;
 +
 +
                br  delay                ; Also do a delay (for some reason)
 +
 +
                ; Delay routine is located earlier in the code:
 +
 +
delay:          li  $FF                  ;$008F - parameter in r5, uses r6
 +
                lr  6, A                ;
 +
dlyloop:        ds  6                    ;
 +
                bnz  dlyloop              ;
 +
                ds  5                    ; Loops another time if value is set in r5
 +
                bnz  delay                ;
  
== Reading the four console buttons ==
+
                pop                      ;
 +
</pre>
  
This code reads the buttons, you can then mask this result similar to the hand controller:
 
  
 +
In your code you can now use the button data to check which buttons/combos are set.
 
<pre>
 
<pre>
; read buttons
 
ins 0 ; get input from port 0
 
com ; invert
 
lr 4, A ; store button result in register 4
 
li 128 ; load timer value for debounce
 
lr 5, A ; in register 5
 
debounce: ; this is a delay to wait until button contacts stops bouncing
 
ds 5
 
bnz debounce ; decrease r5 until zero
 
 
lr A, 4 ; load button result into Ackumulator again
 
lr A, 4 ; load button result into Ackumulator again
 
ni %00000010 ; mask out button #2
 
ni %00000010 ; mask out button #2
Line 165: Line 176:
  
 
<pre>
 
<pre>
 
+
-----4321
 
%00000001  button 1
 
%00000001  button 1
 
%00000010  button 2
 
%00000010  button 2
Line 181: Line 192:
 
%00001110
 
%00001110
 
%00001111  all buttons pressed
 
%00001111  all buttons pressed
 +
</pre>
 +
 +
== Quickest controller read ==
 +
<pre>
 +
readController:
 +
clr
 +
outs 0
 +
outs 1
 +
ins 1
 +
pop
 +
</pre>
  
Combinations are pretty clear.
+
This one only reads data for right controller as port read is faster on the CPU where right controller is hooked up.
 +
Data is not inverted, for an active movement the bit is set to 0, any masking is done in the main program if needed.
  
</pre>
 
  
 
== Conclusion ==
 
== Conclusion ==
Line 209: Line 231:
  
 
<pre>
 
<pre>
 +
clr
 +
outs 0
 
ins 0
 
ins 0
 
com
 
com

Latest revision as of 18:58, 6 August 2020

Wait for controller movement

The simplest use of hand controller is to just pause and wait for a movement, this subroutine does just that, you call it with:

; Add this subroutine call to your code:

	pi	wait.4.controller.input

; Movement from either Hand Controller will continue the program


; here's the actual subroutine, copy and paste to your program where convenient

wait.4.controller.input:
	; see if one of the hand controllers has moved
	clr						; clear accumulator 
	outs	0					; enable input from both hand controllers
	outs	1					; clear latch of port of right hand controller
	outs	4					; clear latch of port of left hand controller
	ins	1					; fetch inverted data from right hand controller
	com						; invert controller data (a %1 now means active)
	bnz	wait.4.controller.input.end		; if no movement then input is 0 -> no branch
	; check the other controller
	ins	4					; fetch inverted data from left hand controller
	com						; invert controller data (if bit is 1 it means active)
	bz	wait.4.controller.input			; if there's no indata repeat

wait.4.controller.input.end:
	pop						; return from subroutine, controller data in A

Controller directions in the register

After the hand controller data has been read from the port latch a 1 means inactive and 0 means active. It's common to want the 1 showing active signal therefore it's inverted with the com opcode as seen above. This is not a necessary step, port data could just as easily be used and save a machine cycle.

The 8 bits are stored in Accumulator (A) and the bits mean this:

        direction
bit  0  right
bit  1  left
bit  2  backward
bit  3  forward
bit  4  counterclockwise
bit  5  clockwise
bit  6  pull up
bit  7  push down


Port-data
%00000001  right
%00000010  left
%00000100  backward
%00001000  forward
%00010000  counterclockwise
%00100000  clockwise
%01000000  pull up
%10000000  push down

Combinations are possible to:
%10000010  push down + left

There are combinations that are impossible with a normal fully functional controller since they are opposite directions:
%11000000  push down + pull up

The "Jet Stick" however could do this as the fire button is parallel to the push down function.

After reading the controller/s it's handy to store it in a register (or in available RAM) for use later
unless checking directly which direction was chosen.
Imagine you have written a game where you can move forward, backward, right or left with the right hand controller.
If we store the result of the controller in register 8, this is how to do it:

Store result for later use

store.right.controller.input:
	
	clr
	outs	0
	outs	1						; check right hand controller
	ins	1
	com							; re-invert controller data
	lr	8, A						; store result in register 8

store.right.controller.input.end:


Later in the program we can load the movement and mask away the directions we're interested in.


	; 	program program
	; 	...
	
	lr	A, 8						; copy register 8 to Ackumulator
	ni	%00001111					; AND result and only keep the last nibble
	lr	8, A						; back up result in r8 again

	;	...
	;	program continues


We now have one of these bit patterns in r8:

%00000000	no movement
%00000001	right
%00000010	left
%00000100	backward
%00000101	backward + right
%00000110	backward + left
%00001000	forward
%00001001	forward + right
%00001010	forward + left

Nothing else is possible with your normal controller unless something is broken.

The controller value can then be compared to the values above to decide what to do next, move the player perhaps.

Reading the four console buttons

There's a routine in the Channel F firmware that can be used directly if needed.
It starts at $00C1, stores the result in register 4, register 5 and 6 are also used.

Port 0 needs to be cleared at least once in the code before reading buttons:

                clr
                outs	0
readbuts:       ins  0                    ; $00C1 - read buttons
                com                       ; Invert port data, 1 now means pushed
                ni   $0F                  ; Just keep button data
                bz   readbuts             ; 
                lr   $4, A                ; Button indata now in r4

                li   $FF                  ; Set r5 to $FF and decrease it
                lr   5, A                 ; 
debounce:       ds   5                    ; 
                bnz  debounce             ; 

                br   delay                ; Also do a delay (for some reason)

                ; Delay routine is located earlier in the code:

delay:          li   $FF                  ;$008F - parameter in r5, uses r6
                lr   6, A                 ;
dlyloop:        ds   6                    ;
                bnz  dlyloop              ;
                ds   5                    ; Loops another time if value is set in r5
                bnz  delay                ;

                pop                       ;


In your code you can now use the button data to check which buttons/combos are set.

	lr	A, 4				; load button result into Ackumulator again
	ni	%00000010			; mask out button #2
	bnz	main.buttons.used.two		; not zero means button 2 was pressed
	lr	A, 4				; load read result into A again
	ni	%00000001			; check if it was button #1
	bnz	main.buttons.used.one		; if that's not 0 it means button 1 was held

	br	main.continue			; continue program

The intelligent reader has already figured out the rest of the inputs available:

-----4321
%00000001  button 1
%00000010  button 2
%00000011
%00000100  button 3
%00000101
%00000110
%00000111
%00001000  button 4
%00001001
%00001010
%00001011
%00001100  button 4 + 3
%00001101
%00001110
%00001111   all buttons pressed

Quickest controller read

readController:
	clr
	outs	0
	outs	1
	ins	1
	pop

This one only reads data for right controller as port read is faster on the CPU where right controller is hooked up. Data is not inverted, for an active movement the bit is set to 0, any masking is done in the main program if needed.


Conclusion

Read right hand controller this way:

	clr
	outs	0
	outs	1
	ins	1
	com		

Read left hand controller this way:

	clr
	outs	4
	ins	4
	com

Read buttons with:

	clr
	outs	0
	ins	0
	com

	; you may need to debounce as described above

And you have the result in A