|Have You Played Atari Today?||2600|5200|7800|Lynx|Jaguar|Forums|Store|
So, moving forward...up until now, we've been making non-interactive demos. A well-coded, graphically impressive demo can be a joy to behold, but our demos haven't been well-coded or graphically impressive. Interaction, obviously, is the hallmark of programming the atari. The most common form of input is the beloved atari joystick. (The same 9-pin format was used for many other systems, such as the Commodore 64 and the Colecovision. (With some enhancements.) In fact, you can use a Sega Genesis controller in an Atari, using the center button for firing.
It turns out that the Atari's controller sockets can be used for input or output. (This might bring to mind the idea of making tons of funky little homebrew electronic interfaces controlled by your Atari. Needless is to say this page of tutorial is not going to get in that deep.) In fact, you can set pins individually for input or output.
The Atari joystick has 4 switches; pressing N,S,E, or W activates one switch, pressing diagonally activates two switches. Before trying to read the direction of the controller, you need to have set the correct I/O Port for "input". (Port A is used for the controllers, Port B for the console switches). You can do this explicitly by writing 0's to all the bits of the memory location SWACNT...or, if you do the "generic start up stuff" routing I've been using, it's one of the memory locations that will be zero'd out in that loop.
Reading the direction, then, is pretty easy. The memory location SWCHA is set as follows:
A zero in a data bit means the joystick has been pressed that direction, and all ones means no joystick is being moved. So if (in binary), you got 11101001, that would mean Player 0 (the left player) was pressing up, and Player 1 (the right player) was pressing down and left. If you read 11001100 you would be confused...someone must be mucking with the hardware.
You may also be interested in whether the firebutton is pressed. Finding that out is easy, but with a few small potential gotchas. When the left joystick button is pressed, D7 (the leftmost bit, used for the sign of the number) of the memory location "INPT4" is set. (INPT5 for the other joystick.) The other bits of the locations are set to random values (well not really, but they probably won't be all zeros so you can't use BEQ/BNE--I was burned by that once) So typical code might look like
LDA INPT4 BMI ButtonNotPressed ;do something here because the button *is* pressed ButtonNotPressedThe other gotcha is that the behavior of this is modified by what you set D6 of our old friend VBLANK to...the safest behavior is to make sure D6 of VBLANK is 0...this is easily done if you just set VBLANK to #2 ( %#00000010 )when you start the vertical blank, since after that is when you'll likely be doing the button checking. (Thanks to Eckhard Stolberg and some other Stella-ites for some advice on the buttons.)
So, for today's program, we'll move yesterday's dot around. Just for kicks, we'll change the screen color when the button is pressed. Again, new stuff is in red, and you might notice the only changes take place during the vertical blank...the kernal is the same as the last lesson's.
; move a dot with the joystick by Kirk Israel processor 6502 include vcs.h org $F000 YPosFromBot = $80; VisibleMissileLine = $81; ;generic start up stuff... Start SEI CLD LDX #$FF TXS LDA #0 ClearMem STA 0,X DEX BNE ClearMem LDA #$00 STA COLUBK ;start with black background LDA #66 STA COLUP0 ;Setting some variables... LDA #80 STA YPosFromBot ;Initial Y Position LDA #$20 STA NUSIZ0 ;Quad Width ;VSYNC time MainLoop LDA #2 STA VSYNC STA WSYNC STA WSYNC STA WSYNC LDA #43 STA TIM64T LDA #0 STA VSYNC ;Main Computations; check down, up, left, right ;general idea is to do a BIT compare to see if ;a certain direction is pressed, and skip the value ;change if so ; ;Not the most effecient code, but gets the job done, ;including diagonal movement ; ; for up and down, we INC or DEC ; the Y Position LDA #%00010000 ;Down? BIT SWCHA BNE SkipMoveDown INC YPosFromBot SkipMoveDown LDA #%00100000 ;Up? BIT SWCHA BNE SkipMoveUp DEC YPosFromBot SkipMoveUp ; for left and right, we're gonna ; set the horizontal speed, and then do ; a single HMOVE. We'll use X to hold the ; horizontal speed, then store it in the ; appropriate register ;assum horiz speed will be zero LDX #0 LDA #%01000000 ;Left? BIT SWCHA BNE SkipMoveLeft LDX #$10 ;a 1 in the left nibble means go left SkipMoveLeft LDA #%10000000 ;Right? BIT SWCHA BNE SkipMoveRight LDX #$F0 ;a -1 in the left nibble means go right... SkipMoveRight ;(in 4 bits, using "two's complement ; notation", binary 1111 = decimal -1 ; (which we write there as hex F -- ; confused?)) STX HMM0 ;set the move for missile 0 ; while we're at it, change the color of the background ; if the button is pressed (making sure D6 of VBLANK has ; appropriately set above) We'll set the background color ; to the vertical position, since that will be changing ; a lot but we can still control it. LDA INPT4 ;read button input BMI ButtonNotPressed ;skip if button not pressed LDA YPosFromBot ;must be pressed, get YPos STA COLUBK ;load into bgcolor ButtonNotPressed WaitForVblankEnd LDA INTIM BNE WaitForVblankEnd LDY #191 STA WSYNC STA VBLANK STA WSYNC STA HMOVE ;main scanline loop... ; ;(this probably ends the "new code" section of today's ; lesson...) ScanLoop STA WSYNC ; here the idea is that VisibleMissileLine ; is zero if the line isn't being drawn now, ; otherwise it's however many lines we have to go CheckActivateMissile CPY YPosFromBot BNE SkipActivateMissile LDA #8 STA VisibleMissileLine SkipActivateMissile ;turn missile off then see if it's turned on LDA #0 STA ENAM0 ; ;if the VisibleMissileLine is non zero, ;we're drawing it ; LDA VisibleMissileLine BEQ FinishMissile IsMissileOn LDA #2 STA ENAM0 DEC VisibleMissileLine FinishMissile DEY BNE ScanLoop LDA #2 STA WSYNC STA VBLANK LDX #30 OverScanWait STA WSYNC DEX BNE OverScanWait JMP MainLoop org $FFFC .word Start .word Start
Next: Happy Face