Quite frequently the amount of information that a programmer wants to display exceeds the amount of information that can fit onto the screen. One way of solving this problem is to scroll the information across the display. For example, listings of BASIC programs scroll vertically from the bottom to the top of the screen. All personal computers implement this type of scrolling. However, the ATARI Home Computer has two additional scrolling facilities that offer exciting possibilities. The first is “Load Memory Scan” (LMS) coarse scrolling; the second is fine scrolling.
Conventional computers use coarse scrolling; in this type of scrolling, the pixels that hold the characters are fixed in position on the screen and text is scrolled by moving bytes through the screen RAM. The resolution of the scrolling is a single character pixel, which is very coarse. The scrolling this produces is jerky and quite unpleasant. Furthermore, it is achieved by moving up to a thousand bytes around in memory, a slow and clumsy task. In essence, the program must move data through the playfield to scroll.
Some personal computers can produce a somewhat finer scroll by drawing images in a higher resolution graphics mode and then scrolling these images. Although higher scrolling resolution is achieved, more data must be moved to attain the scrolling and the program is consequently slowed. The fundamental problem is that the scrolling is implemented by moving data through the screen area.
There is a better way to achieve coarse scrolling with the ATARI 400/800: move the screen area over the data. The display list opcodes support the Load Memory Scan feature. The LMS instruction was first described in Section 2 and tells ANTIC where the screen memory is. A normal display list will have one LMS instruction at the beginning of the display list; the RAM area it points to provides the screen data for the entire screen in a linear sequence. By manipulating the operand bytes of the LMS instruction, a primitive scroll can be implemented. In effect, this moves the playfield window over the screen data. Thus, by manipulating just 2 address bytes, you can produce an effect identical to moving the entire screen RAM. The following program does just that:
CH6PRG1.BAS 10 DLIST=PEEK(560)+256*PEEK(561):REM Find display list 20 LMSLOW=DLIST+4:REM Get low address of LMS operand 30 LMSHIGH=DLIST+5:REM Get high address of LMS operand 40 FOR I=0 TO 255:REM Outer loop 50 POKE LMSHIGH,I 60 FOR J=0 TO 255:REM Inner loop 70 POKE LMSLOW,J 80 FOR Y=1 TO 50:NEXT Y:REM Delay loop 90 NEXT J 100 NEXT I
This program sweeps the display over the entire address space of the computer. The contents of the memory are all dumped onto the screen. The scroll is a clumsy serial scroll combining horizontal scrolling with vertical scrolling. A pure vertical scroll can be achieved by adding or subtracting a fixed amount (the line length in bytes) to the LMS operand. The following program does that:
CH6PRG2.BAS 10 GRAPHICS 0 20 DLIST=PEEK(560)+256*PEEK(561) 30 LMSLOW=DLIST+4 40 LMSHIGH=DLIST+5 50 SCREENLOW=0 60 SCREENHIGH=0 70 SCREENLOW=SCREENLOW+40:REM Next line 80 IF SCREENLOW<256 THEN GOTO 120:REM Overflow? 90 SCREENLOW=SCREENLOW-256:REM Yes, adjust pointer 100 SCREENHIGH=SCREENHIGH+1 110 IF SCREENHIGH=256 THEN END 120 POKE LMSLOW,SCREENLOW 130 POKE LMSHIGH,SCREENHIGH 140 GOTO 70
A pure horizontal scroll is not so simple to do as a pure vertical scroll. The problem is that the screen RAM for a simple display list is organized serially. The screen data bytes for the lines are strung in sequence, with the bytes for one line immediately following the bytes for the previous line. You can horizontally scroll the lines by shifting all the bytes to the left; this is done by decrementing the LMS operand. However, the leftmost byte on each line will then be scrolled into the rightmost position in the next higher line. The first sample program above illustrated this problem.
The solution is to expand the screen data area and break it up into a series of independent horizontal line data areas. Figure 6-1 schematically illustrates this idea:
Figure 6-1 Arranging Screen RAM
On the left is the normal arrangement. One-dimensional serial RAM is stacked in linear sequence to create the screen data area. On the right is the arrangement we need for proper horizontal scrolling. The RAM is of course still one-dimensional and still serial, but now it is used differently. The RAM for each horizontal line extends much further than the screen can show. This is no accident; the whole point of scrolling is to allow a program to display more information than the screen can hold. You can't show all that extra information if you don't allocate the RAM to hold it. With this arrangement you can implement true horizontal scrolling. You can move the screen window over the screen data without the undesirable vertical roll of the earlier approach.
The first step in implementing pure horizontal scroll is to determine the total horizontal line length and allocate RAM accordingly. Next, you must write a completely new display list with an LMS instruction on each mode line. The display list will of course be longer than usual, but there is no reason why you cannot write such a display list. What values do you use for the LMS operands? It is most convenient to use the address of the first byte of each horizontal screen data line. There will be one such address for each mode line on the screen. Once the new display list is in place, ANTIC must be turned onto it and screen data must be written to populate the screen. To execute a scroll, each and every LMS operand in the display list must be incremented for a rightward scroll or decremented for a leftward scroll. Program logic must ensure that the image does not scroll beyond the limits of the allocated RAM areas; otherwise, garbage displays will result. In setting up such logic, the programmer must remember that the LMS operand points to the first screen data byte in the displayed line. The maximum value of the LMS operand is equal to the address of the last byte in the long horizontal line minus the number of bytes in one displayed line. Remember also that the LMS value should not come within one screen display line's length (in bytes) of a 4K address boundary, or the wrong data will be displayed due to LMS counter rollover.
As this process is rather intricate, let us work out an example. First, we must select our total horizontal line length. We shall use a horizontal line length of 256 bytes, as this will simplify address calculations. Each horizontal line will then require one page of RAM. Since we will use BASIC mode 2, there will be 12 mode lines on screen; thus, 12 pages or 3K of RAM will be required. For simplicity (and to guarantee that our screen RAM will be populated with nonzero data), we will use the bottom 3K of RAM. This area is used by the OS and DOS and so should be full of interesting data. To make matters more interesting, we'll put the display list onto page 6 so that we can display the display list on the screen as we are scrolling. The initial values of the LMS operands will thus be particularly easy to calculate; the low order bytes will all be zeros and the high order bytes will be (in order) 0, 1, 2, etc. The following program performs all these operations and scrolls the screen horizontally:
CH6PRG3.BAS 10 REM first set up the display list 20 POKE 1536,112:REM 8 blank lines 30 POKE 1537,112:REM 8 blank lines 40 POKE 1538,112:REM 8 blank lines 50 FOR I=1 TO 12:REM Loop to put in display list 60 POKE 1536+3*1,71:REM BASIC mode 2 with LMS set 70 POKE 1536+3*1+1,0:REM Low byte of LMS operand 80 POKE 1536+3*1+2,1:REM High byte of LMS operand 90 NEXT I 100 POKE 1575,65:REM ANTIC JVB instruction 110 POKE 1576,0:REM Display list starts at $0600 120 POKE 1577,6 130 REM tell ANTIC where display list is 140 POKE 560,0 150 POKE 561,6 160 REM now scroll horizontally 170 FOR I=0 TO 235:REM Loop through LMS low bytes 175 REM we use 235, not 255, because screen width is 20 characters 180 FOR J=1 TO 12:REM for each mode line 190 POKE 1536+3*J+1,I:REM Put in new LMS low byte 200 NEXT J 210 NEXT I 220 GOTO 170:REM Endless loop
This program scrolls the data from right to left. When the end of a page is reached, it simply starts over at the beginning. The display list can be found on the sixth line down (it's on page 6). It appears as a sequence of double quotation marks.
The next step is to mix vertical and horizontal scrolling to get diagonal scrolling. Horizontal scrolling is achieved by adding 1 to or subtracting 1 from the LMS operand. Vertical scrolling is achieved by adding the line length to or subtracting the line length from the LMS operand. Diagonal scrolling is achieved by executing both operations. There are four possible diagonal scroll directions. If, for example, the line length is 256 bytes and we wish to scroll down and to the right, we must add 256+(-1)=255 to each LMS operand in the display list. This is a 2-byte add; the BASIC program example given above avoids the difficulties of 2-byte address manipulations but most programs will not be so contrived. For truly fast two-dimensional scrolling, assembly language will be necessary.
All sorts of weird arrangements are possible if we differentially manipulate the LMS bytes. Lines could scroll relative to each other or hop over each other. Of course, some of this could be done with a conventional display but more data would have to be moved to do it. The real advantage of LMS scrolling is its speed. Instead of manipulating an entire screenfull of data, many thousands of bytes in size, a program need only manipulate two or perhaps a few dozen bytes.
The second important scrolling facility of the ATARI Computer is the fine scrolling capability. Fine scrolling is the capability of scrolling a pixel in steps smaller than the pixel size. (Throughout this section the term pixel refers to an entire character, not to the smaller dots that make up a character.) Coarse scrolls proceed in steps equal to one pixel dimension; fine scrolls proceed in steps of one scan line vertically and one color clock horizontally. Fine scrolling can only carry so far; to get full fine scrolling over long distances on the screen you must couple fine scrolling with coarse scrolling.
There are only two steps to implement fine scrolling. First, you set the fine scroll enable bits in the display list instruction bytes for the mode lines in which you want fine scrolling. (In most cases you want the entire screen to scroll so you set all the scroll enable bits in all the display list instruction bytes.) Bit D5 of the display list instruction is the vertical scroll enable bit; bit D4 of the display list instruction is the horizontal scroll enable bit. You then store the scrolling value you desire into the appropriate scrolling register. There are two scrolling registers, one for horizontal scrolling and one for vertical scrolling. The horizontal scroll register (HSCROL) is at $D404; the vertical scroll register (VSCROL) is at $D405. For horizontal scrolling, you store into HSCROL the number of color clocks by which you want the mode line scrolled. For vertical scrolling, you store into VSCROL the number of scan lines that you want the mode line scrolled. These scroll values will be applied to every line for which the respective fine scroll is enabled.
There are two complicating factors that you encounter when you use fine scrolling. Both arise from the fact that a partially scrolled display shows more information than a normal display. Consider for example what happens when you horizontally scroll a line by half a character to the left. There are 40 characters in the line. Half of the first character disappears off of the left edge of the screen. The 40th character scrolls to the left. What takes its place? Half of a new character should scroll in to take the place of the now scrolled 40th character. This character would be the 41st character. But there are only 40 characters in a normal line. What happens?
If you have implemented coarse scrolling, then the 41st character suddenly appears on the screen after the first character disappears off of the left edge. This sudden appearance is jerky and unsightly. The solution to this problem has already been built into the hardware. There are three display options for line widths: the narrow playfield (128 color clocks wide), the normal playfield (160 color clocks wide) and the wide playfield (192 color clocks wide). These options are set by setting appropriate bits in the DMACTL register. When using horizontal fine scrolling, ANTIC automatically retrieves more data from RAM than it displays. For example, if DMACTL is set for normal playfield, which in BASIC mode 0 has 40 bytes per line, then ANTIC will actually retrieve data at a rate appropriate to wide playfield - 48 bytes per line. This will throw lines off horizontally if it is not taken into account. The problem does not manifest itself if you have already organized screen RAM into long horizontal lines as in Figure 6-1.
The corresponding problem for vertical scrolling can be handled in either of two ways. The sloppy way is to ignore it. Then you will not get half-images at both ends of the display. Instead, the images at the bottom of the display will not scroll in properly; they will suddenly pop into view. The proper way takes very little work. To get proper fine scrolling into and out of the display region you must dedicate one mode line to act as a buffer. You do this by refraining from setting the vertical scroll bit in the display list instruction of the last mode line of the vertically scrolled zone. The window will now scroll without the unpleasant jerk. The screen image will be shortened by one mode line. An advantage of scrolling displays now becomes apparent. It is quite possible to create screen images that have more than 192 scan lines in the display. This could be disastrous with a static display, but with a scrolling display images which are above or below the displayed region can always be scrolled into view.
Fine scrolling will only scroll so far. The vertical limit for fine scrolling is 16 scan lines; the horizontal limit for fine scrolling is 16 color clocks. If you attempt to scroll beyond these limits, ANTIC simply ignores the higher bits of the scroll registers. To get full fine scrolling (in which the entire screen smoothly scrolls as far as you wish) you must couple fine scrolling with coarse scrolling. To do this, first fine scroll the image, keeping track of how far it has been scrolled. When the amount of fine scrolling equals the size of the pixel, reset the fine scroll register to zero and execute a coarse scroll. Figure 6-2 illustrates the process.
Figure 6-2 Linking Fine Scroll to Coarse Scroll
The following program illustrates simple fine scrolling:
CH6PRG4.BAS 1 HSCROL=54276 2 VSCROL=54277 10 GRAPHICS 0:LIST 20 DLIST=PEEK(560)+256*PEEK(561) 30 POKE DLIST+10,50:REM Enable both scrolls 40 POKE DLIST+11,50:REM Do it for two mode lines 50 FOR Y=0 TO 7 60 POKE VSCROL,Y:REM Vertical scroll 70 GOSUB 200:REM Delay 80 NEXT Y 90 FOR X=0 TO 3 100 POKE HSCROL,X:REM Horizontal scroll 110 GOSUB 200:REM Delay 120 NEXT X 130 GOTO 40 200 FOR J=1 TO 200 210 NEXT J:RETURN
This program shows fine scrolling taking place at very slow speed. It demonstrates several problems that arise when using fine scrolling. First, the display lines below the scrolled window are shifted to the right. This is due to ANTIC's automatically retrieving 48 bytes per line instead of 40. The problem arises only in unrealistic demonstration programs such as this one. In real scrolling applications, the arrangement of the screen data (as shown in Figure 6-1) precludes this problem. The second, more serious problem arises when the scroll registers are modified while ANTIC is in the middle of its display process. This confuses ANTIC and causes the screen to jerk. The solution is to change the scroll registers only during vertical blank periods. This can only be done with assembly language routines. Thus, fine scrolling normally requires the use of assembly language.
The applications of full fine scrolling for graphics are numerous. The obvious application is for large maps that are created with character graphics. Using BASIC Graphics mode 2, I have created a very large map of Russia which contains about 10 screenfuls of image. The screen becomes a window to the map. The user can scroll about the entire map with a joystick. The system is very memory efficient; the entire map program plus data plus display list and character set definitions requires a total of about 4K of RAM.
There are many other applications of this technique. Any very large image that can be drawn with character graphics is amenable to this system. (Scrolling does not require character graphics. Map graphics are less desirable for scrolling applications because of their large memory requirements.) Large electronic schematics could be presented in this way. The joystick could be used both to scroll around the schematic and to indicate particular components that the user wishes to address. Large blueprints or architectural diagrams could also be displayed with this technique. Any big image that need not be seen in its entirety can be presented with this system.
Large blocks of text are also usable here, although it might not be practical to read continuous blocks of text by scrolling the image. This system is more suited to presenting blocks of independent text. One particularly exciting idea is to apply this system to menus. The program starts by presenting a welcome sign on the screen with signs indicating submenus pointing to other regions of the larger image. “This way to addition” could point up while “this way to subtraction” might point down. The user scrolls around the menu with the joystick, perusing his options. When he wishes to make a choice, he places a cursor on the option and presses the red button. Although this system could not be applied to all programs, it could be of great value to certain types of programs.
There are two “blue sky” applications of fine scrolling which have not yet been fully explored. The first is selective fine scrolling, in which different mode lines of the display have different scroll bits enabled. Normally you would want the entire screen to scroll, but it is not necessary, to do so. You could select one line for horizontal scrolling only, another line for vertical scrolling only, and so forth. The second blue sky feature is the prospect of using display list interrupts to change the HSCROL or VSCROL registers on the fly. However, changing VSCROL on the fly is a tricky operation; it would probably confuse ANTIC and produce undesirable results. Changing HSCROL is also tricky but might be easier.