|Have You Played Atari Today?||2600|5200|7800|Lynx|Jaguar|Forums|Store|
Congratulations! You've reached the last lesson of 2600 101, and it's a pretty easy one.
Last lesson I said I'd tell you what's wrong with the kernal I presented. If you run that, you might notice that when the happy face is on the left side of the screen, the top line of its head gets chopped off! Why is that? Well, it turns out that even our humble little computations were taking more time than we had...in particular, the CheckActivatePlayer section, where we initialize the variable VisiblePlayerLine with 8, pushes us into visible scanline time, and GRP0 doesn't get set in time
So what to do? This is where the fine art of Atari Kernal Tweaking comes into play. I'm not very good at it, but I'm pretty pleased with the idea I came up...I call it the PlayerBufferStuffertm, patent not pending. If we use a variable to store the player buffer, we can take the entire visible scanline time to do the computations we need for the next line, and then stuff that into the buffer. Then all we have to during the horizontal blank is grab that buffer variable from the last line and stuff it into GRP0.
The technique works pretty well, and can even be expanded for another player or the missiles. It has some disadvantages, like I can't think of it working for multicolored player graphics, but it has one important advantage: even a dumb guy like me can understand it. There are some other tricks that you can use that are game specific as well; for example, if you know one of the players is only going to be on the right side of the screen, you could take your time setting the Register for it, since it would be ok if it bled into the visible scan line.
It turned out to be really easy to make the change from the last example, as you can see by the lack of red below.
; move a happy face with PlayerBufferStuffer processor 6502 include vcs.h org $F000 YPosFromBot = $80; VisiblePlayerLine = $81; PlayerBuffer = $82 ;setup an extra variable ;generic start up stuff... Start SEI CLD LDX #$FF TXS LDA #0 ClearMem STA 0,X DEX BNE ClearMem LDA #$00 ;start with a black background STA COLUBK LDA #$1C ;lets go for bright yellow, the traditional color for happyfaces STA COLUP0 ;Setting some variables... LDA #80 STA YPosFromBot ;Initial Y Position ;; Let's set up the sweeping line. as Missile 1 LDA #2 STA ENAM1 ;enable it LDA #33 STA COLUP1 ;color it LDA #$20 STA NUSIZ1 ;make it quadwidth (not so thin, that) LDA #$F0 ; -1 in the left nibble STA HMM1 ; of HMM1 sets it to moving ;VSYNC time MainLoop LDA #2 STA VSYNC STA WSYNC STA WSYNC STA WSYNC LDA #43 STA TIM64T LDA #0 STA VSYNC ; 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 LDA #%00001000 ;a 1 in D3 of REFP0 says make it mirror STA REFP0 SkipMoveLeft LDA #%10000000 ;Right? BIT SWCHA BNE SkipMoveRight LDX #$F0 ;a -1 in the left nibble means go right... LDA #%00000000 STA REFP0 ;unmirror it SkipMoveRight STX HMP0 ;set the move for player 0, not the missile like last time... ; see if player and missile collide, and change the background color if so LDA #%10000000 BIT CXM1P BEQ NoCollision ;skip if not hitting... LDA YPosFromBot ;must be a hit! load in the YPos... STA COLUBK ;and store as the bgcolor NoCollision STA CXCLR ;reset the collision detection for next time LDA #0 ;zero out the buffer STA PlayerBuffer ;just in case WaitForVblankEnd LDA INTIM BNE WaitForVblankEnd LDY #191 STA WSYNC STA HMOVE STA VBLANK ;main scanline loop... ScanLoop STA WSYNC LDA PlayerBuffer ;buffer was set during last scanline STA GRP0 ;put it as graphics now CheckActivatePlayer CPY YPosFromBot BNE SkipActivatePlayer LDA #8 STA VisiblePlayerLine SkipActivatePlayer ;set player bufferto all zeros for this line, and then see if ;we need to load it with graphic data LDA #0 STA PlayerBuffer ;set buffer, not GRP0 ; ;if the VisiblePlayerLine is non zero, ;we're drawing it next line ; LDX VisiblePlayerLine ;check the visible player line... BEQ FinishPlayer ;skip the drawing if its zero... IsPlayerOn LDA BigHeadGraphic-1,X ;otherwise, load the correct line from BigHeadGraphic ;section below... it's off by 1 though, since at zero ;we stop drawing STA PlayerBuffer ;put that line as player graphic for the next line DEC VisiblePlayerLine ;and decrement the line count FinishPlayer DEY BNE ScanLoop LDA #2 STA WSYNC STA VBLANK LDX #30 OverScanWait STA WSYNC DEX BNE OverScanWait JMP MainLoop BigHeadGraphic .byte #%00111100 .byte #%01111110 .byte #%11000001 .byte #%10111111 .byte #%11111111 .byte #%11101011 .byte #%01111110 .byte #%00111100 org $FFFC .word Start .word Start
Well, that's it, unless I manage to make the Cookbook page I've been thinking of. You now know just about as much as I do about Atari programming, and I hope this tutorial has given you an easier time of it than I had. Check out the resources I list in the Introduction to continue your 2600 programming career... a whole world of kernal tweaking, graphical tricks, and gameplay ideas awaits you.