Classic Computer Magazine Archive A.N.A.L.O.G. ISSUE 67 / DECEMBER 1988 / PAGE 28



Master Memory Map Part V

by Robin Sherer

How to read the Memory Map
    Beginning users: Read the text that is printed in bold type only. These memory locations will be the easiest for you to use and usually don't involve assembly language.
    Advanced users: Read everything! Many areas of memory are not of any practical use, but you can learn a lot about how a computer works by reading the boring parts.

SDMCTL
559     022F

    This location is amazing. So many things can be done here that you'll just flip! Maybe not. Anyway, SDMCTL controls something called Direct Memory Access (DMA). Simply put, DMA is the process by which ANTIC, the Atari graphics chip, gets the information from memory it needs to fill in the screen (information for the playfield and for player/missile graphics). Now this process obviously takes time and slows down the 6502 because of that. So what happens if we turn DMA off? Things will run faster. Try it; POKE 559,0 to turn DMA off. Uh-oh, what happened? The screen went blank. But, of course, with no DMA, ANTIC isn't getting the information for the screen; so there's no picture. What good does a computer do without a picture? Well, sometimes you don't need one. For example, if you're doing a lot of calculations, it's more important to get them done quickly than to have an "I'm thinking" message on the screen, and turning off DMA will speed things up by as much as 30% percent! Of course, with a blank screen the user may think that the computer just up and croaked on him; so be sure you give a warning before the lights go off. SDMCTL is a shadow register for DMACTL (54272).
    By the way, in case you're still sitting there after the POKE 559,0 with a lifeless computer, just press System Reset or type in POKE 559,34. You can't see the POKE written on the screen, but it will still work when you press Return.
    Okay, so we can turn off the screen. Big deal, right? Right, but it's what we can do when we turn the screen back on that counts. SDMCTL lets you make the playfield (the blue screen you PLOT and PRINT onto) wider, narrower or nonexistent. It also lets you turn players and missiles on and off, define how tall you want the players to be and, of course, turn ANTIC on and off.
    Let's take a look at a breakdown of SDMCTL and see what does what (Figure 1). To get players and missiles going, see GRACTL [53277] as well.
    So, to use SDMCTL, pick the options you want, add their values and POKE away. Note that ANTIC must be turned on if you want a display, and you can only have one type of playfield at a time. While we're on the subject of playfields, you'll probably want to know what "narrow" and "wide" playfields are.
    The width of the playfield is measured by something called a "color clock!' A color clock is twice as wide as a pixel in graphics mode 8. That means that a character in graphics mode 0 is four color clocks wide. We'll use graphics mode as an example since you can see the playfield is 160 color clocks wide (40 characters times four color clocks per character). A narrow playfield is only 128 color clocks (32 characters) wide, while a wide playfield has 192 color clocks (48 characters). The television set draws 228 color clocks total (including the black border), but not all of these can be seen. As a matter of fact, not all of the 192 in a wide playfield can be seen either, which makes it good for horizontal scrolling.
    Is having a wide or narrow playfield as easy as it sounds? Well, yes and no: getting it on the screen's easy (try POKE 559,35 right now), using it properly isn't. Unfortunately, telling SDMCTL that you want a different size playfield doesn't tell the OS that anything's different. To see what I mean, try POKE 559,35 from graphics mode zero. Now you have 48 characters per line, but the OS still thinks you have 40. Try typing stuff in, and you'll see the problem. There is no way around this problem, which means that you have to set up the screen memory yourself if you want to take advantage of this feature. Sorry.
    Double-height players, in case you were wondering, have dots the height of those in graphics mode 7, while regularheight players are the height of graphics mode 8. Despite the way I named the two, double-height players are given to you unless you specify otherwise by using SDMCTL.

SDLSTL
560,561     0230,0231

    Another important location, SDLSTL holds the address of the display list. Let's talk display lists.
    We already know about screen memory: the memory locations that hold the information that is to be displayed on the screen (see SAVMSC at 88,89). How does the computer know how to interpret this memory though? As we learned in SDMCTL, there is a special chip called ANTIC that takes care of the graphics. ANTIC has a list of commands that tells it how to display the screen memory. Oddly enough, this list is called the "display list." Since the display list is made up of commands, it's actually like a little program. And, since the screen memory has to be redisplayed 60 times a second, this program is a continuous loop, running over and over again. Why does the screen memory have to be redisplayed? The TV set draws a picture by making different parts of the screen glow at different brightnesses. The screen, however, will only glow for a very short period of time. Therefore, in order to get a picture to stay on the screen, the TV has to draw it 60 times a second.
    For now, let's pretend that a display list is written just like a BASIC program, only with special commands. Let's see what such a display list would look like:

100 DRAW 8 BLANK SCAN LINES
110 DRAW 8 BLANK SCAN LINES
120 DRAW 8 BLANK SCAN LINES
130 CHARACTER MODE 0 LINE...
140 ... WITH SCREEN MEMORY
   STARTING AT [address]
150 CHARACTER MODE 0 LINE
160 CHARACTER MODE 0 LINE



370 CHARACTER MODE 0 LINE
380 GOTO 100 AND WAIT FOR VBLANK


BIT(S)
PATTERN
VALUE
RESULT
0,1
------00
0
No playfield

------01
1
Narrow playfield

------10
2
Regular playfield

------11
3
Wide playfield
2
-----0--
0
Missles on

-----1--
4
Missles on
3
----0---
0
Players off

----1---
8
Players on
4
---0----
0
Double height players

---1----
16
Regular height players
5
--0-----
0
ANTC off

--1-----
32
ANTIC on
6,7
--------

Not used

FIGURE 1: SDMCTL chart

    We start by telling ANTIC to leave the first 24 scan lines blank. Scan lines are the height of a graphics mode 18 line, start before the left edge of the screen and go all the way past the right edge. If you look closely at the screen, you can even see them. We leave 24 lines blank so that we can be sure that all of our picture will be on everyone's screen. TV and monitor screens all act slightly differently; so the blank lines will create a frame that can cover the edges of the screen. These blank lines make up the top of the black border that you can see in graphics mode 0.
    Next we want to start our mode 0 lines, so we have a mode 0 line command. ANTIC has to know where the screen memory is before it can start drawing; so we make the first mode 0 command a special one that tells ANTIC that the address of the screen memory will come next. Then, after the screen memory address, we have 23 regular mode 0 commands. Finally, we tell ANTIC to go back to the beginning and start all over again after VBLANK. Remember that VBLANK is the time during which the TV is getting ready to start drawing the picture again. We want to make sure it's ready before ANTIC starts again; so we tell ANTIC to wait until VBLANK is over.
    Now that we have a basic understanding, let's look at the specifics. First of all, ANTIC doesn't use line numbers. In a real display list, the line numbers would be memory locations. Secondly, ANTIC has abbreviations for all the commands. And thirdly, there is no thirdly. Let's therefore look at the proper way to write the preceding display list. We'll have it start at location 1000 (decimal), although it would usually be much higher in memory:


1000 BLK 8
1001 BLK 8
1002 BLK 8
1003 CHR 2 LMS
1004 < screen memory low byte >
1005 < screen memory high byte >
1006 CHR 2
1007 CHR 2
        •
        •
        •
1028 CHR 2
1029 JVB 1000

    As you can see, this isn't that much different from our original. LMS, by the way, stands for Load Memory Scan and tells ANTIC that the next two bytes will be the address of the beginning of screen memory. Now, the final step is to convert these commands into numbers that we can POKE into memory. There is a unique number assigned to each command, and Figures 2 and 3 give you those numbers. Before you look at that chart, however, let me explain the other commands that you'll see there:


    MAP is the same as CHR, except it's used to indicate a graphics mode rather than a character (text) mode.

    JMP is like JVB, except it doesn't tell ANTIC to wait for the end of VBLANK. It's needed because of a quirk in ANTIC that says a display list can't cross a 1K boundary. What's a 1K boundary? It's a memory location that's a multiple of 1024. With display lists created by graphics commands, this is no problem. If you're designing your own, however, and you have to cross such a boundary, JMP over it. While we're on the topic of boundaries, you should also be aware that screen memory is not allowed to cross a 4K boundary. If it does, you have to have a second LMS instruction to get past the boundary. Under normal circumstances, however, this only happens in graphics modes 8 through 11 and the OS will take care of it for you.

    HSC like LMS, is not really a command but rather a modification to a command. It tells ANTIC that this mode line is to have the capability of fine horizontal scrolling. See HSCROL at 54276.

    VSC you guessed it, is another modification that specifies a fine vertical-scrolling capability. See VSCROL (54277).

    DLI is the fourth modification, telling ANTIC that there is to be a display list interrupt at the end of this mode line. Pay particular attention to the "end." See VDSLST (512,513) for more details on DLIs.

    Now that you'll be able to understand the chart, why don't you go take a quick peek at it.
    Run the following program to take a look at the actual graphics mode 0 display list as it is stored in memory:

100 GRAPHICS 0
110 DLIST=PEEK(560)+PEEK(561)*256
120 PRINT PEEK(DLIST)
130 IF PEEK(DLIST)<>65 THEN DLIST=D
LIST+1:GOTO 120
140 PRINT PEEK(DLIST+1)
150 PRINT PEEK(DLIST+2)
160 END

    Use CTRL-1 to pause the display list. Notice that the last two numbers PRINTed (the address for the JVB) are the same as the values in 560 and 561. If you can't figure out why, drink a couple of cups of coffee and read this whole description all over again. Here is a hint: They tell the computer to go back and use the same display list all over again. If you change the numbers here, the computer will use another display list at the new address, which means you could use several display lists at once.

Load Memory Scan (LMS) is a powerful
tool that has no steadfast rules about its
usage; use your creativity.

    We've pretty much covered the standard graphics display lists, but what about custom ones? It should have occurred to you by now that you can write your own display lists, mixing different graphics modes on the screen.
    A few words before we move on. What can you use LMS for other than to tell ANTIC where the screen memory is? Nothing! You can, however, tell ANTIC where the screen memory is more than once in the same display list. Why would you want to do that? Well, you have to if you're fine scrolling (see HSCROL and VSCROL). You could also do it to repeat the same line over and over again without wasting memory. LMS is another powerful tool that has no steadfast rules about what to use it for; use your creativity. Here's an example of repeating text. After the program has run, clear the screen and try typing in a line of text.

100 GRAPHICS 0
110 DLIST=PEEK(561)*256+67:POKE 560
,67
120 LOW=PEEK(88)
130 HIGH=PEEK(89)+2
136 POKE 89,HIGH
137 POKE DLIST,112
138 POKE DLIST+1,112
139 POKE DLIST+2,112
140 DLIST=DLIST+3
150 FOR ROW=0 TO 23
160 POKE DLIST,66
170 POKE DLIST+1,LOW
180 POKE DLIST+2,HIGH
190 DLIST=DLIST+3
200 NEXT ROW
210 POKE DLIST,65
220 POKE DLIST+1,PEEK(560)
230 POKE DLIST+2, PEEK(561)

    What's going on here? Essentially we're rewriting a graphics mode 0 display list so that all the lines have LMSes that point to the same address. We also have to change SAVMSC (88,89), so that the OS knows where our new screen memory is. Why isn't the new screen memory in the same place as the old screen memory? Because our new display list overflows into the old screen memory, that's why.
    If you press System Reset, a normal graphics mode 0 screen will appear.

SSKCTL
562     0232

    SSKTL is used to control the serial port and is a shadow register for SKCTL (53775). As your state of confusion should indicate to you, it is not really a location for the inexperienced. Look at SKCTL if you are interested; look at the OS manual if you're really interested.

Noname
563     0233

    This location is currently unused. Atari reserves the right to use it in future versions of the OS, so don't count on it being safe to use.

LPENH
564     0234

    Ever hear of a light pen? The thing that looks like a pen, that you can draw on the screen with and so forth? Well, if you happen to be one of the lucky few who have one, this will tell you what horizontal position on the screen it's pointing to. Neat, huh?
    LPENH is a shadow register for PENH (54284).

LPENV
565     0235

    This is the vertical position of the light pen on the screen. It's a shadow register for PENV (54285).
    Since light pens defy all reason, a few words about them are probably in order here. Firstly, LPENH and LPENV are set when the light pen is pressed to the screen. LPENV gets the value of VCOUNT (54283) when the pen was pressed (VCOUNT gets incremented by one every two scan lines). LPENH, on the other hand, gets a value based on the number of color clocks that have been drawn so far. See SDLSTL for an explanation of a "color clock." Now I'd be more than happy to give you the range of values that LPENH and LPENV will return as you move a light pen about the screen, but unfortunately the values depend on several things. Run the following program to see what the limits are for your computer. Also see STICKO-3 (632-635).

100 GRAPHICS 0
105 POKE 752,1
110 POSITION 2,11
120 PRINT "Light pen horizontal pos
ition = "
130 PRINT "          vertical posit
ion = "
140 POSITION 34,11
150 PRINT PEEK(564);"  ";
160 POSITION 32,12
170 PRINT PEEK(565);"  ";
180 GOTO 140

    You should also note that light pens are not very precise. The values can vary slightly even if you hold it steadily at one point on the screen.
    If you're writing a program that uses the light pen, be sure to allow for a little variation in the values. This can be done by "sampling" values. Basically, this means read ten or so values every time you need one, throw out the highest and lowest and average the rest to get a single value.

BRKKEY
566,567     0236,0237

    In the "B" version of the OS, this is the vector for the BREAK key interrupt. It is initialized to 59220, which means you will find 84 in location 566 and 231 in location 567. To disable the break key, POKE 143 into 566.
    See BRKKEY at location 17 if you want to write your own BREAK key routine and you have the old OS. Also see locations 512 through 535 for more information on interrupt vectors.

Noname
568,569     0238,0239

    More "unused" bytes. See the warning at location 563 about such bytes.
    Okay, we're going back to I/O now. The next four bytes make up the Command Frame Buffer (CFB), a table that SIO uses when doing serial bus operations. Remember that the serial bus is what information travels back and forth on. It is not designed to be used by you; so you should be reading out of curiosity rather than necessity.


CDEVIC
570     023A

    CDEVIC contains the device code.

CCOMND
571     023B

    CCOMND contains the command code.

CAUX1
572     023C

    CAUX1 contains the command auxiliary byte 1, which comes from DAUX1 at location 778.

CAUX2
573     023D

    Finally, CAUX2 contains the command auxiliary byte 2, which SIO gets from DAUX2 at location 779.

TEMP
574     023E

    SIO uses TEMP as a TEMPorary storage place (the people who name these things are so clever).

ERRFLG
575     023F

    If any error occurred during device I/O, with the exception of a time-out, ERRFLG is set to 255. Otherwise, if everything is okay, it is set to 0.
    Also see STATUS at location 48.

DFLAGS
576     0240

    When a disk is booted (the computer is turned on with the disk drive on), the first disk "record"-a segment of information that has been recorded on the disk-is read from sector 1 into memory. A sector is a segment of the disk, shaped like a piece of pie. Some of the information from this record is used to continue the boot. The first byte in the record contains several useful flags. It gets stored in DFLAGS.

DBSECT
577     0241

    The second byte tells how many sectors are used in the boot file. It gets stored in DBSECT.

BOOTAD
578,579     0242,0243

    Finally, because we have to know where to put the file, the third and fourth bytes give the starting address. They get stored in BOOTAD. Once the OS knows BOOTAD, it moves the record it just read in over to the new address and starts loading in the rest of the file, putting each record after the previous one until the whole file is properly in memory.
    BOOTAD gets transferred to RAMLO (4,5) which, because it is in page 0, is used to move the file from the sector buffer to its place in memory.
    In most cases, the boot file is DOS, and BOOTAD will hold the address 1792.

COLDST
580     0244

    COLDST is fun. The OS sets it to one during the power-up process, and then sets it to zero when everything has been properly initialized. If somebody presses System Reset, the OS looks at COLDST to see whether it was in the middle of power-up, when System Reset was pressed. If it was (COLDST equals one), then the turkey who did the pressing messed up the powerup, and the OS has to start all over again. Otherwise, the OS just treats it like a normal reset.
    Fun? That's your idea of fun? Hey, let me finish. The OS isn't too smart; COLDST is the only way it knows whether or not it's in the middle of power-up when System Reset is pressed. That means you can set COLDST to one in your program, and if System Reset gets pressed, the computer will act as if somebody just turned the computer on. Your whole program will be erased rather than broken into (usually System Reset will cause the OS to jump to BASIC, where your program can be LISTed or SAVEd).
    Use COLDST along with POKMSK (16) and STMCUR (138,139) to totally protect your BASIC program from being looked at or SAVEd. The disk or cassette they're on could still be copied though. Another good use for this trick is to POKE 580,1 and press System Reset instead of using your On/Off switch when you want to load in another program. It saves wear and tear on the computer.

Noname
581     0245

    Yet another unused byte for which the warning at location 563 applies.

DSKTIM
582     0246

    The disk time-out register. We last saw time-outs at location PTIMOT (28). Well, they're back, this time for the disk drive (PTIMOT was for the printer). DSKTIM holds the time-out value for the FORMAT command. It is supposedly initialized to 160, but I have seen machines that initialize it to 120 and 224, which I suspect has something to do with different versions of the OS. Anyway, regardless of what it's initialized to, the value in DSKTIM is updated after every STATUS request with the value in DVSTAT+2 (748).
    You should look at DTIMLO (774) for lots more information on disk time-out, including the exact use for DSKTIM.

LINBUF
583-622     0247-026E

    Hey, remember buffers? This is a 40-bytelong buffer used by the screen editor. You see, the screen editor needs a place to temporarily store a line of text when it's moving stuff around on the screen. This is it.

    ADDRESS (100,101) is used as a temporary zero-page pointer to LINBUF during the moving process.

In the wonderful world of Atari graphics,
there are two kinds of things that can appear on the
screen: the playfield and player/missile
graphics.

GPRIOR
623     026F

    GPRIOR is used to set priorities and to select GTIA modes.
    In the wonderful world of Atari graphics, there are two kinds of things that can appear on the screen. First of all, there is the playfield. The playfield is what you get by PRINTing, PLOTing and DRAWTOing. The playfield is made up of as many as five colors, which are specified by the color registers (as in SETCOLOR color register, color, brightness). Each of these colors represents a different part of the playfield. The one with the same color as color register 0 is called playfield 0 and so forth. The one with the background color (color register 4) is called BAK.
    The second type of thing that can appear on the screen is player/missile graphics. We'll be getting more into players and missiles in the CTIA/GTIA chip at location 53248, but for now just be aware that there are four players and four missiles that can appear on the screen at the same time as the playfield.
    So, where is all of this getting us? Well, if you have player/missile graphics on the screen, and you also have a playfield, which should be seen when the two are in the same place? In other words, which should have priority over the other? GPRIOR tells ANTIC, the chip that draws the picture, who has the highest priority (i.e., who gets to be seen). Look at the chart in Figure 4.
    "PF" means PlayField, "P" means Player. Missiles have the same priority as the player with the same number. Keep in mind that something with a higher priority will appear to move in front of something with a lower priority. Similarly, of course, something with a lower priority will appear to move behind something with a higher priority.
    As you probably noticed, only the last four bits of GPRIOR are used to set the priority (make sure only one of those four bits is on). What about the other four? If you set bit 4, then all the missiles will have the same color as playfield 3. That lets you move the missiles together and use them as a fifth player (P4). If you set bit 5, then overlapping player 0 and player 1 will produce a third color in the overlap area. This goes for player 2 and player 3 as well. For machine languagers, or just the curious, the third color is produced by ORing the colors of the two players together.
    As long as we're on the subject of overlap colors, if you do set more than one of the last four bits, then in a case where two overlapping objects have the same priority, the overlap area will be black.
    In graphics modes 0 and 8, only the color of the text or pixels will be changed if a player or missile flies over them. The brightness will not change.
    So now we're left with the seventh and eighth bits. They don't have anything to do with priorities or player/missile graphics. Instead they indicate whether or not a GTIA mode is being used, and if so, which one. They work as shown in Figure 5.
    You only need to set these bits if you're writing your own display list. Otherwise the OS will take care of them when you use BASIC to call "GR.9, 10 or 11."
    GPRIOR is a shadow register for PRIOR (53275).

GPRIOR


----0001
(0)
----0010
(2)
----0100
(4)
---1000
(8)
BIT PATTERN
(VALUE)
HIGHER   P0
  P0 PF0 PF0
PRIORITY    P1   P1 PF1 PF1

  P2
PF0
PF2
  P0


  P3
PF1
PF3+P4
  P1


PF0
PF2   P0
  P2


PF1 PF3+P4   P1
  P3


PF2   P2
  P2
PF2

LOWER PF3+P4   P3
  P3
PF3+P4

PRIORITY BAK
BAK BAK BAK

FIGURE 4: GPRIOR chart

    The next 24 locations (624-647) hold information about the joysticks, paddles and light pens.

PADDL0
624     0270

    PADDL0 holds the current value of paddle 0 (the left paddle in the leftmost plug in the front of the computer). Paddles can have a value ranging from 0 to 228; the further you turn the paddle clockwise, the higher the value.
    Paddles are actually little more than a "potentiometer." Let's look at a potentiometer as though it were a bathroom faucet. The computer sends a value 255 worth of water into the faucet, but the amount that comes out depends on how far open the faucet is. If it's all the way open (the paddle is turned clockwise as far as it will go), then almost all of the water will flow through (228 worth). If it's all the way closed (the paddle is turned counterclockwise as far as it will go), then none of the value will flow through. And that's exactly how a potentiometer works, except the computer is sending electricity into it rather than water (paddles aren't waterproof).
    If you design a program that uses the paddles, be careful. Most of the time you won't want them to have a range of 0 to 228. For example, if you're using the paddles to move a player, the player will probably move off the screen. So what can you do to get the range that you want? Let's suppose you want to go from LOW to HIGH. First, do the following:


RANGE=HIGH-LOW+1
EACH=RANGE/228

    These two lines should come somewhere in your program before you start using the paddles. Then, every time you read the paddle, do the following:

MYVAL=INT(OLDVAL*EACH+.5)+LOW

where OLDVAL is the value of the paddle, and MYVAL is the corresponding number in the range you wanted.
    PADDL0 is a shadow register for POT0 at location 53760. Note that the value for the button (trigger) on the paddle can be found at PTRIG0 (636).
    The following paddle locations work the same as paddle 0, but you change the number of the paddle (of course). Paddles 0 and 1 plug into the leftmost port (hold) numbered 1 on the front of the Atari computer. Likewise 6 and 7 plug into the rightmost port numbered 4 on the computer.

If  you design a program that uses the
paddles, be careful. Most of the time you
won't want them to have a range of 0 to 228.

PADDL1
625     0271

    The value for paddle 1 and a shadow register for POT1 at 53761.

PADDL2
626     0272

    The value for paddle 2 and a shadow register for POT2 at 53762.

PADDL3
627     0273

    I'll leave this and the next four for you to figure out. Hint: see the last three locations.

PADDL4
628     0274

PADDL5
629     0275

PADDL6
630     0276

PADDL7
631     0277

STICK0
632     0278

    Let's see. PADDL0 was paddle 0, so I wonder what STICK0 is? Could it possibly be joystick 0? Despite all the odds against it, it is. Joystick 0 is the one plugged into the leftmost plug in the front of the computer.
    Unlike paddle values, joystick values don't appear at first (or second) to make much sense. Let's take a look at those values (Figure 6).

00--------     (0) No GTIA mode
01--------   (64) GRAPHICS 9
10-------- (128) GRAPHICS 10
11-------- (192) GRAPHICS 11
                
FIGURE 5: GTIA chart




FIGURE 6: Joystick values in decimal


    This figure represents the nine possible positions the joystick can be in, along with the value corresponding to each. If you move joystick 0 up, for example, STICK0 will have a value of 14. Now, unless you have some brilliant power of observation that I don't, these values don't seem to make any sense. I mean, does "14" mean "up" to you? Not to me. They must make some kind of sense to the computer, however, so let's take a look at them again (Figure 7), this time in binary, the way the computer sees them.



FIGURE 7: Joystick values in binary

    It may not be immediately obvious, but now things make sense. Notice how the first bit (the digit on the right) of each value is only equal to zero when the joystick is up (straight up or diagonally up)? And the second bit is only zero when it's down, the third when it's left and the fourth when it's right. So we get Figure 8.
    And that's why the numbers don't make sense when you first look at them.

---0    means "up"
---1    means "not up"
--0-    means "down"
--1-    means "not down"
-0--    means "left"
-1--    means "not left"
0---    means "right"
1---    means "not right"

FIGURE 8: Joystick bit chart


    Here are a couple of machine-language routines to help you make a little more sense out of the joystick values. One looks for vertical movement and will return a zero for up, one for center and two for down. The other looks for horizontal movement and returns a zero for left, one for center and two for right. As you'll see from the following example, these values can prove to be very practical:


100 DIM STICKV$(19):DIM STICKH$(22)
110 FOR CHAR=1 TO 19
120 READ CODE
130 STICKV$(CHAR,CHAR)=CHR$(CODE)
140 NEXT CHAR
150 FOR CHAR=1 TO 22
160 READ CODE
170 STICKH$(CHAR,CHAR)=CHR$(CODE)
180 NEXT CHAR
200 GRAPHICS 0:POKE 752,1
210 PRINT :PRINT "Machine language
joystick example"
220 POSITION 10,4:PRINT "VERTICAL V
ALUE:"
230 POSITION 8,6:PRINT "HORIZONTAL
VALUE:"
240 VERT=USR(ADR(STICKV$),0)-1
250 HORZ=USR(ADR(STICKH$),0)-1
260 POSITION 26,4:PRINT VERT;"
 "
270 POSITION 26,6:PRINT HORZ;"
 "
280 GOTO 240
1000 DATA 104,104,133,213,104,170,1
89,120,2,41,3
1010 DATA 201,2,240,1,74,133,212,96
2000 DATA 104,104,133,213,104,170,1
89,120,2,74,74
2010 DATA 73,2,201,3,208,2,169,2,13
3,212,96

    If you wanted to read joystick 1 instead of joystick 0, you'd use USR(ADR(STICKV$),1) and USR(ADR (STICKH$),1).
    Here's the assembly code that's stored in the DATA statements:


68    STICKV PLA
68           PLA
8505         STA $D5
68           PLA
AA           TAX
BD7802       LDA STICK0,X
2903         AND #$03
C902         CMP #$02
F001         BEQ DONE
4A           LSR A
85D4  DONE   STA $D4
60           RTS
68    STICKH PLA
68           PLA
85D5         STA $D5
68           PLA
AA           TAX
BD7802       LDA STICK0,X
4A           LSR A
4A           LSR A
4902         EOR #$02
C903         CMP #$03
D002         BNE DONE1
A902         LDA #$02
85D4  DONE1  STA $D4
60           RTS

    STICK0 is a shadow register for the last four bits (the leftmost four) of PORTA at location 54016. It is set to a value other than 15 when a light pen in the leftmost controller jack is pressed on the screen.

STICK1
633     0279

    Same as STICK0 except it's a shadow register for the first four bits of PORTA rather than the last two. It's also the value for joystick 1 rather than joystick 0.





HSC


VSC
HSC
VSC

HSC


VSC
HSC
VSC


HSC


VSC
HSC
VSC

HSC


VSC
HSC
VSC






LMS LMS LMS LMS



LMS LMS LMS LMS










DLI
 
DLI DLI DLI DLI DLI DLI DLI
BLK
1  
00 







80







BLK 2
10







90







BLK 3
20







A0







BLK 4
30







B0







BLK 5
40







C0







BLK 6
50







D0







BLK 7
60







E0







BLK 8
70







F0







JMP

01







81







JVB

41







C1







CHR
2
02
12
22
32
42
52
62
72
82
92
A2
B2
C2
D2
E2
F2
CHR 3
03
13
23
33
43
53
63
73
83
93
A3
B3
C3
D3
E3
F3
CHR 4
04
14
24
34
44
54
64
74
84
94
A4
B4
C4
D4
E4
F4
CHR 5
05
15
25
35
45
55
65
75
85
95
A5
B5
C5
D5
E5
F5
CHR 6
06
16
26
36
46
56
66
76
86
96
A6
B6
C6
D6
E6
F6
CHR 7
07
17
27
37
47
57
67
77
87
97
A7
B7
C7
D7
E7
F7
MAP
8
08
18
28
38
48
58
68
78
88
98
A8
B8
C8
D8
E8
F8
MAp 9
09
19
29
39
49
59
69
79
89
99
A9
B9
C9
D9
E9
F9
MAP A
0A
1A
2A
3A
4A
5A
6A
7A
8A
9A
AA
BA
CA
DA
EA
FA
MAP B
0B
1B
2B
3B
4B
5B
6B
7B
8B
9B
AB
BB
CB
DB
EB
FB
MAP C
0C
1C
2C
3C
4C
5C
6C
7C
8C
9C
AC
BC
CC
DC
EC
FC
MAP D
0D
1D
2D
3D
4D
5D
6D
7D
8D
9D
AD
BD
CD
DD
ED
FD
MAP E
0E
1E
2E
3E
4E
5E
6E
7E
8E
9E
AE
BE
CE
DE
EE
FE
MAP F
0F
1F
2F
3F
4F
5F
6F
7F
8F
9F
AF
BF
CF
DF
EF
FF

FIGURE 2: Display list command chart, hex version





HSC


VSC
HSC
VSC

HSC


VSC
HSC
VSC


HSC


VSC
HSC
VSC

HSC


VSC
HSC
VSC






LMS LMS LMS LMS



LMS LMS LMS LMS










DLI
 
DLI DLI DLI DLI DLI DLI DLI
BLK
1
0







128







BLK 2
16







144







BLK 3
32







160







BLK 4
48







176







BLK 5
64







192







BLK 6
80







208







BLK 7
96







224







BLK 8
112







240







JMP

1







129







JVB

65







193







CHR
2
2
18
34
50
66
82
98
114
130
146
162
178
194
210
226
242
CHR 3
3
19
35
51
67
83
99
115
131
147
163
179
195
211
227
243
CHR 4
4
20
36
52
68
84
100
116
132
148
164
180
196
212
228
244
CHR 5
5
21
37
53
69
85
101
117
133
149
165
181
197
213
229
245
CHR 6
6
22
38
54
70
86
102
118
134
150
166
182
198
214
23
246
CHR 7
7
23
39
55
71
87
103
119
135
151
167
183
199
215
231
247
MAP
8
8
24
40
56
72
88
104
120
136
152
168
184
200
216
232
248
MAp 9
9
25
41
57
73
89
105
121
137
153
169
185
201
217
233
249
MAP 10
10
26
42
58
74
90
106
122
138
154
170
186
202
218
234
250
MAP 11
11
27
43
59
75
91
107
123
139
155
171
187
203
219
235
251
MAP 12
12
28
44
60
76
92
108
124
140
156
172
188
204
220
236
252
MAP 13 
13
29
45
61
77
93
109
125
141
157
173
189
205
221
237
253
MAP 14
14
30
46
62
78
94
110
126
142
158
174
190
206
222
238
254
MAP 15
15
31
47
63
79
95
111
127
143
159
175
191
207
223
239
255

FIGURE 3: Display list command chart


STICK2

634     027A

    Same as STICK1 except it's for joystick 2, and it's also a shadow register for the last four bits of PORTB (54017).

STICK3
635     027B

    Joystick 3 (the rightmost one) value and a shadow register for the last four bits of PORTB.

PTRIG0
636     027C

    If you press the trigger on paddle zero, PTRIG0 will have a value of zero. If you don't press it, PTRIG0 will have a value of one.
    PTRIG0 through PTRIG3 get their values from bits 2, 3, 6 and 7 of PORTA(54016), respectively. Because these are the same bits that tell whether joysticks 1 and 2 are moved to the right or left (see STICK0), you can use the trick in Figure 9.



PTRIG(1)-PTRIG(0)=-1 if joystick zero is moved to the left
                 =0 it joystick zero is in the center
                 =1 If joystick zero is moved to the right

FIGURE 9: PTRIG chart


    The same holds true for PTRIG(3)PTRIG(2) and joystick 1. You can use this trick to make horizontal movement easier to program. Just add the value of the PTRIG difference to your old horizontal position. This saves trying to figure out the joysticks. For the same ease in vertical movement, use the routine given for STICK0.

PTRRG1
637     027D

    Same as PTRIG0 but for paddle 1.

PTRIG2
638     027E

    Trigger value for paddle 2.

PTRIG3
639     027F

    Trigger value for paddle 3.

PTRIG4
640     0280

    Trigger value for paddle 4.
    PTRIG4 through PTRIG7 get their values from bits 2, 3, 6 and 7 of PORTB (54017), respectively. The same trick for horizontal movement that was described under PTRIG0 can be applied to joystick 2 and joystick 3 using PTRIG4 through PTRIG7.

PTRIG5
641     0281

    Trigger value for paddle 5.

PTRIG6
642     0282

    Trigger value for paddle 6.

PTRIG7
643     0283

    Guess.

STRIG0
644     0284
    Well, here we are again at another new, different and challenging name for a location. For the next three as well, actually. All three STRIG locations hold the values for the joystick button, and work exactly the same way as PTRIG (zero means pressed).
    The STRIGS are shadow registers for the TRIGS (53264 to 53267).

STRIG1
645     0285

STRIG2
646     0286

STRIG3
647     0287