Home > Atari Memories > #Assembly > Atari 2600 Programming for Newbies

Atari 2600 Programming for Newbies

Session 21: Sprites

By Andrew Davie (adapted by Duane Alan Hahn, a.k.a. Random Terrain)

As an Amazon Associate I earn from qualifying purchases. (I get commissions for purchases made through certain links on this page.)

Page Table of Contents

Original Session

It's time to begin our understanding of sprites.


What are sprites? By now, sprites are well-known in the gaming industry. They are small, independently movable objects which are drawn by hardware anywhere over the top of playfield graphics. The Atari 2600 was the first console to introduce general-purpose spritesback in the day they were called 'player missile graphics'. It was the Commodore 64 which introduced the term 'sprites', which we know and love.


The Atari 2600 has two 'players', two 'missiles' and a 'ball'all of these are sprites, and each has various parameters which can be adjusted by the programmer (position, size, color, shape, etc). We're going to concentrate, this session, on the 'players' and how they work.


Player graphics have much finer resolution than playfield graphics. Each player is 8 pixels wide, and each pixel in a player is just a single TIA color-clock in width. In other words, the pixels in player graphics are a quarter of the width of the pixels in playfield graphics. The graphics of each player are controlled by a single 8-bit TIA register. The register for player 0 (the first player) is GRP0 (standing for 'Graphics, Player 0') and the register for the second player is GRP1. When you write data to either of these registers you change the visuals of the relevant player sprite being drawn on the screen.


Just like playfield graphics, the player graphics registers only hold a single 'line' of data. If you do not modify the data on-the-fly (that is, changing it every scanline), then the TIA just displays the same data on every scanline. So kernels using sprite graphics typically modify these player graphics registers constantly.


Surprisingly, though player sprites can be (effectively) positioned anywhere on the screen, they do NOT have position registers. Most more modern machines (Nintendo, C64, etc.) provided an x,y coordinate which was used to position a sprite on the screen. The Atari 2600 is a much more primitive beast.


The horizontal position of a player sprite is controlled by writing to a 'reset position' register (RESP0 for sprite 0 and RESP1 for sprite 1). When you write to these registers, you cause the hardware to begin drawing the relevant sprite . . . immediately! This is very strange and a bit hard to get used to at first. To move a sprite horizontally to any x-position on a scanline, one has to make sure that the RESP0 write happens just before the position on the scanline at which you want the sprite to appear. Since the 6502 is running at 1/3 of the clock speed of the TIA, this makes it incredibly difficult to write to RESP0 at exactly the right time. For every cycle of 6502 time, three pixels (cycles of TIA time) pass. So it's only possible to position sprites (through RESPx writes) with an accuracy of 1 6502 clock period, or in other words three TIA pixels.


To facilitate fine-positioning of sprites, the TIA has additional registers which allow the sprite to be adjusted in position by a few pixels. We are not going to cover that this sessionbut instead we'll have a look at how sprite graphics are written, how the course RESPx registers are used, and how sprite colors are controlled. Fine positioning of sprites is an art in itself, and many solutions have been proposed on the [stella] list. We'll get to that in a session or two, but for now, let's stick with the basics.


The sample kernel shows a fully working sprite demo.


There are very few additions from our earlier playfield demos. . .

                lda #$56

                sta COLUP0

                lda #$67

                sta COLUP1

In our initialization (before the main frame loop) the above code is initializing the colors of the two player sprites. These are random purplish colors. You may also change the color on-the-fly by rewriting it every scanline. Remember, thoughyou only have 76 cycles per scanlineso there's only so much you can cram into a single line before you run out of 'space'.


                SLEEP 20

                sta RESP0

                SLEEP 10

                sta RESP1

                stx GRP0               ; modify sprite 0 shape

                stx GRP1

                sta WSYNC


                cpx #184

                bne MiddleLines

The above code sample is the 'guts' of our sprite demo. It doesn't do a lot of new stuff. You should already be familiar with the SLEEP macroit just causes a delay of a certain number of 6502 cycles. The purpose of the SLEEP macros here is to delay to a position somewhere in the middle of the scanlineyou may play with the values and see the effect on the positioning of the sprites.


Immediately after each SLEEP, there's a write to RESPx for each of the player sprites. This causes the TIA to begin drawing the appropriate player sprite immediately. And what will it draw?

                stx GRP0               ; modify sprite 0 shape

                stx GRP1

Since, in this kernel, the x register is counting the scanline number, that is also the value written to both of the graphics registers (GRPx) for the player sprites. So the graphics we see will change on each scanline, and it will represent a visual image of the scanline counter. This should be pretty evident by the image below:

Here's the sample kernel:







That's pretty much all there is to getting sprites up and running. There are a few interesting things we need to cover in the coming sessions, including sprite size, sprite repeating, priorities, buffered sprite drawing, drawing specific images/shapes and lots of other stuff. But now you have the basics, and you should be able to do some experimenting with what you see here.


See you next time!







The answer to each question below is hidden in a black box that has a green border. Hover your mouse pointer over the box (or tap it if you're using something like an iPhone) to reveal the answer.


  1. Modify the kernel so that the color of the sprite is changed every scanline. How many cycles does this add to your kernel? How many cycles total is each of your lines taking now?
  2. It takes 3 cycles per write to a color register (eg: stx COLUP1), but it takes two or more additional cycles if you want to load a specific color. The variation in time depends on the addressing mode you use to load the color (eg: an immediate value = 2 cycles, but loading indirectly through a zero page pointer to a memory location, indexed by the y register, would take 6 cycles!).

        lda #34         ; 2
        sta COLUP1      ; 3
        lda (colour),y  ; 6
        sta COLUP1      ; 3
  3. Instead of using the scanline to write the shape of the sprite, load the shape from a table. Can you think how it would be possible to draw (say) a Mario-shaped sprite anywhere on the screen? This is tricky, so we'll devote a session or more to vertical positioning.
  4. This really is too tricky to answer here. Future sessions will cover this problem thoroughly, as its fundamental to drawing sprites in your game.

  5. What happens when you use more than 76 cycles on a linehow will this code misbehave?
  6. Remember that the TIA and the TV beam are in synch. The timing is such that precisely 76 cycles of 6502 time, or 228 cycles of TIA time, correspond to *exactly* one scanline on the TV. Currently we've been using "sta WSYNC" to synchronize our kernel to the start of every scanline. This isn't necessary IF our code makes sure that our kernel lines take EXACTLY 76 cycles to execute.


    But since the above code DOES use "sta WSYNC", a 3 cycle instruction, we really only have 73 cycles per line available for other processing. If we exceed these 73 cycles, then that pushes the "sta WSYNC" past the point at which it's on the current scanline and onto the point where it's really on the NEXT scanline. And if it happens on the NEXT scanline, it will operate as expected (and that, as we know, is by halting the 6502 until the start of the NEXT scanline).


    So essentially, if our code exceeds 76 cycles, then each scanline will actually be two scanlines deep! And instead of sending, say, 262 scanlines per frame, we'd be sending 524. Most TVs cannot cope with this and they will, as noted, 'roll'. I just wanted you to understand WHY.

  7. The picture shows sprites over the 'border' areas at top and bottom, yet the code which draws sprites is only active for the middle section. Why is this happening? How would you prevent it?
  8. A good lesson in how the TIA works. The TIA registers hold whatever you put into them, until you next put something in to them. So after our last write to the sprite registers, the TIA keeps displaying the same shape for sprites, on each scanline, until we write again. So what we're really seeing in those border areas is the last write (which is actually at the bottom of the changing shape area of sprites) repeated on the bottom, and then on the top again, until we start writing sprite shapes again.


    The solution is to write 0 to GRP0 and GRP1 when we've finished drawing our spritesand, of course, on initialization of the system.

  9. Move the SLEEP and RESPx code outside the middle loopplace this code BEFORE the loop. What differences would you expect to see? Is the result surprising?
  10. Barring minor timing changes which will cause the positions to shift slightly, the effect I was trying to show was that it is not necessary to rewrite the RESPx registers every scanline. You only need to position your sprites once each, and they will remain in that position until you reposition them. By moving the reposition outside the loop, we've freed up extra cycles in the kernel code for each scanline.


    Positioning sprites to any arbitrary horizontal position is quite complex, and usually takes at least one whole scanline to do in a generic fashion. This is why games which use multiple sprites rarely allow those sprites to cross over each other, and also the reason why you see distinct 'bands' of sprites in other gamesthe gaps between the bands is where the horizontal movement code is doing its stuff.




Other Assembly Language Tutorials

Be sure to check out the other assembly language tutorials and the general programming pages on this web site.


Amazon Stuff


< Previous Session



Next Session >





Session Links

Session 1: Start Here

Session 2: Television Display Basics

Sessions 3 & 6: The TIA and the 6502

Session 4: The TIA

Session 5: Memory Architecture

Session 7: The TV and our Kernel

Session 8: Our First Kernel

Session 9: 6502 and DASM - Assembling the Basics

Session 10: Orgasm

Session 11: Colorful Colors

Session 12: Initialization

Session 13: Playfield Basics

Session 14: Playfield Weirdness

Session 15: Playfield Continued

Session 16: Letting the Assembler do the Work

Sessions 17 & 18: Asymmetrical Playfields (Parts 1 & 2)

Session 19: Addressing Modes

Session 20: Asymmetrical Playfields (Part 3)

Session 21: Sprites

Session 22: Sprites, Horizontal Positioning (Part 1)

Session 22: Sprites, Horizontal Positioning (Part 2)

Session 23: Moving Sprites Vertically

Session 24: Some Nice Code

Session 25: Advanced Timeslicing





Useful Links

Easy 6502 by Nick Morgan

How to get started writing 6502 assembly language. Includes a JavaScript 6502 assembler and simulator.



Atari Roots by Mark Andrews (Online Book)

This book was written in English, not computerese. It's written for Atari users, not for professional programmers (though they might find it useful).



Machine Language For Beginners by Richard Mansfield (Online Book)

This book only assumes a working knowledge of BASIC. It was designed to speak directly to the amateur programmer, the part-time computerist. It should help you make the transition from BASIC to machine language with relative ease.



The Second Book Of Machine Language by Richard Mansfield (Online Book)

This book shows how to put together a large machine language program. All of the fundamentals were covered in Machine Language for Beginners. What remains is to put the rules to use by constructing a working program, to take the theory into the field and show how machine language is done.



6502 Instruction Set with Examples

A useful page from Assembly Language Programming for the Atari Computers.

Continually strives to remain the largest and most complete source for 6502-related information in the world.



Guide to 6502 Assembly Language Programming by Andrew Jacobs

Below are direct links to the most important pages.



Stella Programmer's Guide

HTMLified version.



Nick Bensema's Guide to Cycle Counting on the Atari 2600

Cycle counting is an important aspect of Atari 2600 programming. It makes possible the positioning of sprites, the drawing of six-digit scores, non-mirrored playfield graphics and many other cool TIA tricks that keep every game from looking like Combat.



How to Draw A Playfield by Nick Bensema

Atari 2600 programming is different from any other kind of programming in many ways. Just one of these ways is the flow of the program.



Cart Sizes and Bankswitching Methods by Kevin Horton

The "bankswitching bible." Also check out the Atari 2600 Fun Facts and Information Guide and this post about bankswitching by SeaGtGruff at AtariAge.



Atari 2600 Specifications

Atari 2600 programming specs (HTML version).



Atari 2600 Programming Page (AtariAge)

Links to useful information, tools, source code, and documentation.




Atari 2600 programming site based on Garon's "The Dig," which is now dead.



TIA Color Charts and Tools

Includes interactive color charts, an NTSC/PAL color conversion tool, and Atari 2600 color compatibility tools that can help you quickly find colors that go great together.



The Atari 2600 Music and Sound Page

Adapted information and charts related to Atari 2600 music and sound.



Game Standards and Procedures

A guide and a check list for finished carts.




A multi-platform Atari 2600 VCS emulator. It has a built-in debugger to help you with your works in progress or you can use it to study classic games.




A very good emulator that can also be embedded on your own web site so people can play the games you make online. It's much better than JStella.



batari Basic Commands

If assembly language seems a little too hard, don't worry. You can always try to make Atari 2600 games the faster, easier way with batari Basic.



Atari 2600 BASIC

If assembly language is too hard for you, try batari Basic. It's a BASIC-like language for creating Atari 2600 games. It's the faster, easier way to make Atari 2600 games.

Try batari Basic

Back to Top



In Case You Didn't Know


Trump's Jab = Bad

Did you know that Trump's rushed experimental rona jab has less than one percent overall benefit? It also has many possible horrible side effects. Some brainwashed rona jab cultists claim that there are no victims of the jab, but person after person will post what the jab did to them or a family member on web sites such as Facebook and Twitter and they'll be lucky if they don't get banned soon after. Posting the truth is “misinformation” don't you know. Awakened sheep might turn into lions, so powerful people will do just about anything to keep the sheep from waking up.


Check out these videos:

Best of Sped Up and Edited Tennessee House of Representatives Health Subcommittee Hearing Room 2 (March 1, 2022)

Full Video of Tennessee House of Representatives Health Subcommittee Hearing Room 2 (The Doctors Start Talking at 33:28)



H Word and I Word = Good

Take a look at my page called The H Word and Beyond. You might also want to look at my page called Zinc and Quercetin. My sister and I have been taking those two supplements since summer of 2020 in the hopes that they would scare away the flu and other viruses (or at least make them less severe).



B Vitamins = Good

Some people appear to have a mental illness because they have a vitamin B deficiency. For example, the wife of a guy I used to chat with online had severe mood swings which seemed to be caused by food allergies or intolerances. She would became irrational, obnoxious, throw tantrums, and generally act like she had a mental illness. The horrid behavior stopped after she started taking a vitamin B complex. I've been taking #ad Jarrow B-Right for many years. It makes me much easier to live with.



Soy = Bad

Unfermented soy is bad! “When she stopped eating soy, the mental problems went away.” Fermented soy doesn't bother me, but the various versions of unfermented soy (soy flour, soybean oil, and so on) that are used in all kinds of products these days causes a negative mental health reaction in me that a vitamin B complex can't tame. The sinister encroachment of soy has made the careful reading of ingredients a necessity.



Wheat = Bad

If you are overweight, have type II diabetes, or are worried about the condition of your heart, check out the videos by Ken D Berry, William Davis, and Ivor Cummins. It seems that most people should avoid wheat, not just those who have a wheat allergy or celiac disease. Check out these books: #ad Undoctored, #ad Wheat Belly, and #ad Eat Rich, Live Long.



Negative Ions = Good

Negative ions are good for us. You might want to avoid positive ion generators and ozone generators. Whenever I need a new air cleaner (with negative ion generator), I buy it from A plain old air cleaner is better than nothing, but one that produces negative ions makes the air in a room fresher and easier for me to breathe. It also helps to brighten my mood.



Litterbugs = Bad

Never litter. Toss it in the trash or take it home. Do not throw it on the ground. Also remember that good people clean up after themselves at home, out in public, at a campsite and so on. Leave it better than you found it.



Climate Change Cash Grab = Bad

Seems like more people than ever finally care about water, land, and air pollution, but the climate change cash grab scam is designed to put more of your money into the bank accounts of greedy politicians. Those power-hungry schemers try to trick us with bad data and lies about overpopulation while pretending to be caring do-gooders. Trying to eliminate pollution is a good thing, but the carbon footprint of the average law-abiding human right now is actually making the planet greener instead of killing it.


Watch these two YouTube videos for more information:

CO2 is Greening The Earth

The Climate Agenda


View this page and any external web sites at your own risk. I am not responsible for any possible spiritual, emotional, physical, financial or any other damage to you, your friends, family, ancestors, or descendants in the past, present, or future, living or dead, in this dimension or any other.


Use any example programs at your own risk. I am not responsible if they blow up your computer or melt your Atari 2600. Use assembly language at your own risk. I am not responsible if assembly language makes you cry or gives you brain damage.


Home Inventions Quotations Game Design Atari Memories Personal Pages About Site Map Contact Privacy Policy Tip Jar