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.
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 ; Inverse port data, 1 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.
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