Difference between revisions of "Reading Controllers"
(→Store result for later use) |
(→Reading the four console buttons) |
||
(8 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: | ||
− | |||
− | |||
− | |||
<pre> | <pre> | ||
− | ; your | + | ; Add this subroutine call to your code: |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
+ | pi wait.4.controller.input | ||
+ | ; Movement from either Hand Controller will continue the program | ||
Line 27: | Line 18: | ||
outs 0 ; enable input from both hand controllers | outs 0 ; enable input from both hand controllers | ||
outs 1 ; clear latch of port of right hand controller | 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 | ins 1 ; fetch inverted data from right hand controller | ||
− | com ; | + | com ; invert controller data (a %1 now means active) |
bnz wait.4.controller.input.end ; if no movement then input is 0 -> no branch | bnz wait.4.controller.input.end ; if no movement then input is 0 -> no branch | ||
; check the other controller | ; check the other controller | ||
− | |||
− | |||
ins 4 ; fetch inverted data from left hand controller | ins 4 ; fetch inverted data from left hand controller | ||
− | com ; | + | 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: | wait.4.controller.input.end: | ||
− | pop ; return from subroutine</pre> | + | pop ; return from subroutine, controller data in A |
+ | </pre> | ||
== Controller directions in the register == | == Controller directions in the register == | ||
− | After the hand controller data has been read | + | After the hand controller data has been read from the port latch a 1 means inactive and 0 means active. |
− | The 8 bits | + | 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: | ||
<pre> | <pre> | ||
Line 56: | Line 49: | ||
bit 6 pull up | bit 6 pull up | ||
bit 7 push down | bit 7 push down | ||
− | |||
− | |||
− | |||
Port-data | Port-data | ||
%00000001 right | %00000001 right | ||
Line 72: | Line 62: | ||
Combinations are possible to: | Combinations are possible to: | ||
− | |||
%10000010 push down + left | %10000010 push down + left | ||
There are combinations that are impossible with a normal fully functional controller 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. | 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 | + | 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> | ||
+ | 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 135: | Line 125: | ||
== Reading the four console buttons == | == 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> | ||
+ | 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 ; | ||
+ | </pre> | ||
+ | |||
+ | In your code you can now use the button data to check which buttons/combos are set. | ||
<pre> | <pre> | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
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 160: | Line 176: | ||
<pre> | <pre> | ||
− | + | -----4321 | |
%00000001 button 1 | %00000001 button 1 | ||
%00000010 button 2 | %00000010 button 2 | ||
Line 176: | 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> | ||
− | + | 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 == | == Conclusion == | ||
Line 204: | Line 231: | ||
<pre> | <pre> | ||
+ | clr | ||
+ | outs 0 | ||
ins 0 | ins 0 | ||
com | com |
Latest revision as of 20:58, 6 August 2020
Contents
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