Learn to Implement Page Flipping in BASIC on Atari 8-Bit Computers (30-60 mins)

Task: Learn to Implement Page Flipping in BASIC on Atari 8-Bit Computers

Needed: Altirra emulator

Time: 30-60 mins

Introduction

We previously covered moving data on and off the screen as part of coarse horizontal and vertical scrolling by changing the memory address of the screen data using the load memory scan (LMS) feature of the display list. This same feature can be used to rapidly flip between different whole screens of data by systematically changing the starting memory location of each screen in the LMS instruction of the display list. Screen flipping can be used for a number of special graphics effects including animation and mixing colors. It could also be used to change the playfield as a player walks off the screen as was used in the game Maze Maniac published in Antic Magazine.

We demonstrate here page flipping in BASIC with the aid of some assembly code executed during a vertical blank interrupt. Executing the page flipping routine during the vertical blank makes the flipping effect much smoother and more consistent than doing it in BASIC. We will use the assembly method in this demo.

There are four main components to page flipping. The first is the screen memory where the multiple page or screens to be flipped will reside. The second is the display list which needs to be modified to include the location of the first screen. The third is the assembly code to perform the flipping. The final component is a vertical blank interrupt to call the assembly code after the screen has been drawn. We describe each of these components below along with the corresponding BASIC and assembly code.

Screen Memory

When a Graphics command is issued in BASIC a segment of memory is reserved called screen memory just below the top of RAM. This is where characters for text modes and pixels for graphics modes are stored. In addition, a display list is created and stored just below the top of RAM with a pointer to beginning of screen memory for when ANTIC draws the screen. For the purpose of page flipping we need to manually reserve two or more screens of data and keep track of their memory locations so we can tell the display list and ANTIC where to find them. The easiest way to do this is to move the top of RAM back and use the free memory above the new RAM top as a place to put our screen data. We describe this below with the BASIC code after summarizing the initialization for the program.

Before we setup the screen we must first do some initializations in lines 10 to 90. Line 20 is the POKE which moves the top of RAM back four pages or 1024 bytes. This makes room for our four pages of screen data. Lines 30 to 50 initialize a preliminary Graphics 2 screen to let the user know there is a short delay while the screen data is being loaded into memory. This screen and its display list will not be used further. Line 60 set up variables for our new display list address on page 6 of memory (DL=1536). The high byte or page (6) and low byte (0) are also stored in variables for later use by the display list. Line 70 sets up a string where we will store the assembly code for the vertical blank interrupt routine to be loaded later. Line 80 initializes our vertical blank delay counter and stores it in page zero memory location 203 for easy access by our assembly language routine where it will be used. Line 90 stores our page counter in memory location 204. This will keep track of which page is to be displayed during the flipping process.

10 REM *** INITIALIZATIONS ***
20 POKE 106,PEEK(106)-4
30 GRAPHICS 2+16
40 SETCOLOR 0,0,14:SETCOLOR 4,7,0
50 PRINT #6;”LOADING SCREEN DATA”
60 DL=1536:DLHI=6:DLLO=0
70 DIM VBI$(10)
80 POKE 203,30:REM Vertical blank counter for delay
90 POKE 204,0:REM Page counter to keep track of flips

Lines 100 to 180 setup the four screen of data above the new top of RAM established by line 20. Line 110 initializes SCR as a variable with the memory location of the first screen of data. This is located in the first page of memory right above the top of RAM. We get the page number of this memory location using PEEK(106) and store it in the variable SCRHI. The low byte is zero because the screen data starts at the beginning of this page of memory. Line 120 stores the high byte of the screen memory in page zero memory location 205 for easy access by our assembly language program. Lines 130 to 180 write the screen data to each of the four pages of memory (incremented by 256 bytes per page). The first page ends up full of the number 1 with the second page full of 2s, etc. These are the screens we will flip. The screen of 3s (line 160) is shown below. Note that each screen has 12 rows * 20 columns = 240 bytes of screen data.

100 REM *** SETUP SCREEN MEMORY ABOVE RAMTOP ***
110 SCRLO=0:SCRHI=PEEK(106):SCR=SCRHI*256+SCRLO
120 POKE 205,SCRHI:REM Store high byte in memory 205
130 FOR I=0 TO 239
140 POKE SCR+I,17:REM SCREEN 1 (1s)
150 POKE SCR+256+I,18:REM SCREEN 2 (2s)
160 POKE SCR+512+I,19:REM SCREEN 3 (3s)
170 POKE SCR+768+I,20:REM SCREEN 4 (4s)
180 NEXT I

Page 3 of Flipping Demo
Page 3 of Flipping Demo

Display List

The display list is the program the ANTIC chip uses to draw the screen. In addition to specifying the graphics mode of each line, the display list can also specify the memory address of the screen data using the load memory scan (LMS) instruction. This can be done once on the first mode line to specify the start of memory for the whole screen or for each mode line separately as we needed to do for horizontal scrolling.

Normally, the display list consists of the graphics mode number (ANTIC value) for each mode line (e.g. Graphics 2 is ANTIC ‘7’ in display list). We must add a 64 to the number specifying the graphics mode for the LMS instruction which then needs to be followed by two additional lines specifying the low byte and then the high byte of the screen memory address for that mode line. In our BASIC example, this is our new top of RAM or PEEK(106) for the high byte and 0 (start of that page of memory) for the low byte. The following is a description of the display list in our demo BASIC program. Recall that we are storing the display list on page 6 of memory (i.e. DL = 1536 = 6 * 256).

Lines 240 to 260 start the display list and specify the required three blank mode lines not shown on the screen. We POKE 112 for the blank lines to memory locations DL, DL+1, and DL+2 (i.e. memory locations 1536, 1537, and 1538). Line 250 is where we add 64 for the LMS instruction to ANTIC mode 7 followed by two additional POKEs specifying the low byte (SCRLO) and the high byte (SCRHI) of our our page of screen data defined back on line 110. Line 260 specifies the next 11 lines of ANTIC mode 7 lines. Line 270 signals the end of the display list and a jump to the vertical blank before drawing the next screen (we will use this period to execute our page flipping code). Line 280 provides the low and then high bytes of the memory location where the display list is stored (here it is high byte 6 for page 6 of memory and low byte of 0). Line 290 tells the Atari where the display list is so it doesn’t use the one it created when we did the Graphics 2 command back on line 30 for the intro screen. Note that the SCRHI value of the POKE DL+5 on line 250 is the one we will increment by one each time to flip pages. After four pages are flipped this get reset back to its original value (i.e. POKE(106)) stored away in memory location 205 as specified on line 120 above. This is the essence of page flipping!

200 REM *** SETUP DISPLAY LIST ON PAGE 6 OF MEMORY ***
210 REM *** WITH LMS FOR FIRST PAGE OF SCREEN DATA ***
220 POKE DL+0,112
230 POKE DL+1,112
240 POKE DL+2,112
250 POKE DL+3,7+64:POKE DL+4,SCRLO:POKE DL+5,SCRHI
260 FOR I=6 TO 16:POKE DL+I,7:NEXT I
270 POKE DL+18,65
280 POKE DL+19,DLLO:POKE DL+20,DLHI
290 POKE 560,DLLO:POKE 561,DLHI

Assembly Code for Page Flipping

As described above, assembly code executed during a vertical blank interrupt is necessary for smooth page flipping unencumbered by the inefficiencies of BASIC. The assembly code for our page flipping routine is shown below and included as a text file. The leftmost column are the assembly language mnemonics. The second column are decimal values for the assembly commands which we will include in DATA statements in BASIC (see lines 1000 to 1030) and then READ into memory (Page 6 + 20, i.e. right after the display list) for execution by the vertical blank interrupt routine. The third column contains detailed comments about what each line does. This code is only for making the BASIC DATA statements and is not meant to be compiled using an assembler.

There are three parts to the page flipping routine. The first four lines of code is a simple delay routine which will skip the scrolling routines for a certain number of vertical blank periods (in this case 30) to slow down the flipping. You can slow it down further by changing this value on lines 80 and 1010. Once the delay is over, the main page flipping routine starts. Detailed comments about each assembly mnemonic are provided in the figure below. Briefly, we increment the page counter and compare it to the number 4 (we flip when the counter is 0, 1, 2, or 3). A 4 indicates flipping is done and we resent the counter back to 0. We then get the high byte of the start of the first page of our screen memory (i.e. POKE(106)) and add to it our counter value to advance forward in memory to the next page when necessary. We store this updated value in the memory location (1541 or page 6 + 5, i.e. DL+5) corresponding to the LMS high byte for mode line one of our display list (i.e. line 250 above). Once we are done with the flip for this vertical blank period we exit back to BASIC for another round of ANTIC drawing the screen with our newly declared screen of data. This code is executed once per vertical blank after each drawing of the screen. The counters help us keep track of how many vertical blanks to skip for the delay and which page of data needs to be drawn.

Assembly Code for Page Flipping
Assembly Code for Page Flipping

Below is our BASIC code for reading in the assembly code specified above and in the text file. Each mnemonic and its argument gets a decimal value that we list in the data statements (lines 1000 to 1030) and read into page 6 + 20 of memory using line 320.

300 REM *** READ IN ASSEMBLY CODE FOR FLIPPING ***
310 REM *** STORE IN P6 MEMORY LOCATION 1536+I ***
320 FOR I=20 TO 50:READ DAT:POKE 1536+I,DAT:NEXT I

1000 REM *** CODE FOR PAGE FLIPPING DURING VBI ***
1010 DATA 198,203,208,24,169,30,133,203
1020 DATA 230,204,165,204,201,4,208,2,169,0,133,204
1030 DATA 165,205,24,101,204,141,5,6,76,98,228

The key to making this work is to execute the assembly code described above during the vertical blank so BASIC code we write for our application can’t slow it down in an inconsistent manner from screen to screen. For this we need a vertical blank interrupt (VBI) routine. I have covered how to implement a VBI in assembly in a previous post. The assembly code for the VBI is very similar to the code presented in that post. Briefly, we specify a deferred VBI by loading the accumulator with a 7 and then specify the starting memory location of our assembly code by loading the high byte in the X register and the low byte in the Y register. Note that the high byte is the same page 6 address. However, we are using the first 19 bytes of page 6 for our display list. We will start the location of our scrolling code at low byte 20 of page 6.

The decimal values for this code can be found in lines 2000 and 2010 of the BASIC code. We read the code into our VBI$ string in BASIC lines 400 and 410. We execute the VBI code just once in line 510.

400 REM *** READ CODE FOR VERTICAL BLANK INTERRUPT ***
410 FOR I=1 TO 10:READ DAT:VBI$(I)=CHR$(DAT):NEXT I

500 REM *** SHORT & SWEET MAIN LOOP! ***
510 VBI=USR(ADR(VBI$))
520 GOTO 520

2000 REM *** CODE FOR VERTICAL BLANK INTERRUPT ***
2010 DATA 104,169,7,162,6,160,20,76,92,228

Once the VBI has been executed there is nothing left for our BASIC program to do than loop indefinitely on line 520. Our assembly code for flipping will be executed by the operating system in the deferred vertical blank interrupt every time ANTIC finishes drawing the screen.

Instructions

Here is the ATR file and a text file with the BASIC code the horizontal scrolling demo. This is a DOS 2.0S disk image. The filename of the BASIC code is FLIP.BAS. Here is the assembly language file shown above.

Step 1

Open Altirra and load BASIC. I used Turbo BASIC XL but any flavor of BASIC compatible with Atari BASIC will do. You can also load the built-in BASIC from Altirra from the File->Attach special cartridge menu.

Step 2

Open the BASIC code in your PC text editor or web browser and copy the text to your clipboard. Click on the View tab of Altirra and choose the paste text option from the bottom of the list. Altirra will slowly paste the text into the BASIC command line. Note you can go to the System tab and select “Warp Speed” for fast pasting. It is important to keep focus on the Altirra window or the paste will stop and it will lose some characters.

Step 3

Also try booting the ATR file in Altirra or on your Atari from your PC using SIO2PC. You can also load the ATR on a FujiNet or similar device and boot from there. You must have a BASIC cartridge loaded. You can load the file from BASIC using LOAD “D:FLIP.BAS” and then type RUN.

Comments

Once you are comfortable with how screen memory and display lists work, page flipping is really pretty easy. One way to think about this is that it is scrolling in the third dimension. The neat thing about flipping is that the screen doesn’t need to be drawn each time with print or plot statements. By pointing the display list to the memory location the screen is displayed instantaneously by the ANTIC chip at a speed equal to the cathode ray tube beam. Fast flipping can create interesting animations not possible with player-missile graphics. The main downside is that each screen can take up a lot of memory. There is also a limit to how many screens you can flip smoothly given the screen is drawn only 60 times per second. Further, there are some memory boundary conflicts with the high resolution graphics models.

If you want to try this in pure BASIC you can simply replace all the assembly code with a set of POKEs to the display list with the different high byte memory addresses. You can cycle through these one at a time with a delay controlled by a FOR loop. I have tried this and it works well as long as you don’t have other things going on in your BASIC program which could interfere with the timing.

Here are some additional sources for reading. Here is a nice tutorial by David Plotkin from a piece in Antic magazine. I used the basic concept of his assembly routine for inspiration when I wrote mine.

Here is a piece in Compute! by Clay Stuart.

Maze Maniac is a game published by Antic Magazine which uses page flipping.

The book Advanced Programming Techniques for Your Atari by Linda Shreiber has a whole chapter on page flipping (Chapter 9).