DISPLAY LIST INTERRUPTS -
Pt. III APPLICATIONS
by Bob Cockroft
Pt. III APPLICATIONS
by Bob Cockroft
The following article is the conclusion of two earlier articles on display list interrupts that appeared in ROM issues 2 & 3. Although it is not necessary, it may be helpful to have read these earlier articles if you have not previously been introduced to display list interrupts. Also, it is assumed that you have a basic understanding of hexadecimal machine code and lo/hi byte configurations.
Display List Interrupts is one of the more powerful techniques that can be used to manipulate screen displays. With only a few adjustments, the Atari's colour capabilities can be expanded. Graphic modes plus "players" and "missiles" can be modified to have many extra colours. But display list interrupts will do much more than provide additional colours. They can also be used to enhance the power of P/M graphics. The number of "players" and "missiles" can be increased. In addition, it is possible to create a single "player" that has a number of different widths. Character graphics can also be improved. By changing the CHBASE pointer during the time the screen is being drawn, the character set can be changed part way down the screen. Therefore, it is possible to have both a normal and a modified character on the screen at the same time.
It is the purpose of this article to explain how to apply display list interrupts. To do this we must briefly backtrack to material covered in the earlier articles in this series. Every graphics or text display the computer draws has a corresponding program that tells the Antic chip how to set up the screen. Although it keeps the same format, this program, called the DISPLAY LIST (DL), varies somewhat with each graphic mode. The base address for the display list is contained in the display list pointers in lo/hi byte form. Obtain the base address by converting the value in these pointers into decimal form. To do this multiply the high byte by 256 and add it to the low byte. (see below)
The first 3 bytes of the display list place 24 blank lines at the top of the screen. The next 3 tell where the screen data is located. The following bytes are the major component of the DL and are the ones we are interested in. These next bytes are called the mode bytes. Each mode byte displays one horizontal line of graphics and changes both in number and in value with each graphic mode. It is important to know that the computer reads the display list in order, starting from the beginning. As a result, the computer first draws one horizontal line of graphics at the top of the screen. Then more horizontal lines are drawn at progressively lower levels until the screen is filled. The table below displays each graphic mode and its corresponding mode byte value.
|Graphic Mode||Mode byte value|
There are three steps in making a a simple display list interrupt that divides the screen into two differently coloured halves.(note: the screen will be divided horizontally) The first step is to indicate to the computer at what vertical level you want the division to take place. This is done by adding 128 to the mode byte that corresponds to the horizontal level of graphics where you want the division to occur. Suppose you want to tell the computer to divide a Graphics 0 screen. Remembering that each mode byte corresponds to one line of graphics from top to bottom, you are able to find its dividing point by counting down the mode byte list until you reach the point where you want the division. To indicate an interrupt add 128 to this mode byte. For example, if the dividing point were to be in the middle of the screen, you would need to add 128 to the middle mode byte. (see below)
Assume Graphics 0
DLB = Display List Base Address
REM * the 2 is the mode byte for Graphics 0
The second step is to make a subroutine that tells the computer what to do during the interrupt. The subroutine will need to be in machine code and could be stored anywhere in free RAM. The location we will use in this article will be 1536(dec)-also known as "PAGE 6". To indicate to the computer where the machine code is located, the VDSLST pointers (dec 512,513) must be set to the beginning address of the subroutine. To follow our example, assume that the subroutine is stored at location 1536(dec). Convert this address into hexadecimal form. It is best dividing it by 256 and adding 2 decimal places.(see below)
1536/256=6+00+600 HEXADECIMAL ($6 00)
Remembering that VDSLST pointer is in lo/hi byte form, set it to the 600 hex.
POKE 512,0:POKE 513,6
Before we are able to make this subroutine there are a few things you will need to know about COLOR registers.
The Atari computer has two different types of COLOR registers; hardware and shadow. The hardware registers are the "real" ones, that is, they are the ones which the computer consults for colour determination. The hardware registers are updated by their corresponding shadow registers every time after the computer draws a new screen. Because the computer is continually doing this, the hardware registers are always being updated. In this way any value in the shadow register will be stored in the hardwa re register almost immediately.
|Hardware Register||Shadow Register|
It will be the purpose of the machine language subroutine to change the colour of the background during the time in which the computer draws the screen. This will result in the screen being horizontally divided into two differently coloured halves. When the computer reads the display list it will encounter the mode byte you set, back in step 1. At this point, the computer will jump to your machine code. It is here the subroutine will change the background COLOR hardware register. As a result, the bottom half of the screen will be in a new hue. When the computer completes drawing the screen, the shadow register will update the hardware register to its initial colour. As a consequence, the computer will begin to draw the top half of a new screen with the original colour. When the modified mode byte is again read, the computer will jump to the subroutine so that the process is repeated. By human standards, this entire routine is practically instantaneous, taking only a few fractions of a second.
|Machine Language Subroutine|
|1536||72||PHA ;PUSH `A' ON THE STACK|
||TXA ;TRANSFER X TO A|
||PHA ;PUSH `A' ON THE STACK|
||LDA ;LOAD `A'|
||# 1 ;WITH ANY NUMBER|
||STA ;AVOID CHANGE|
||$0A ;IN MIDDLE OF|
||STA ;STORE NEW COLOUR|
||$1A ;IN HARDWARE|
||PLA ;REPLACE `A'|
||TAX ;TRANSFER A TO X|
The third and final step is enabling a non-maskable interrupt. (NMI) This is simply done by POKEing 54286 (dec) with 192.
Now that the principles of display list interrupts have been explained, we can attempt some practical applications. Many Atari computer enthusiasts believe they are confined to using a maximum of 4 PLAYERS and MISSILES the P/M graphics system has allotted. Fortunately this belief is not necessarily true. Display list interrupts can horizontally cut a PLAYER column into a number of different sections. Each of these sections will be able to assume its own unique horizontal position. The result is a number of PLAYERS being created from a single original. To make things even better, all that applies to PLAYERs also applies to MISSILES. There is, unfortunately, one draw back. Because all the newly created PLAYERs are formed from one PLAYER column, it is impossible for any of the new PLAYERs to vertically overlap one another. The problem is that any byte of PLAYER column can display only one image on the screen. It cannot be at two places at once. This makes vertical overlapping impossible. The diagram below should display what I have been explaining.
Diagram 1P/M Graphics
¶ ¶----------¶ ¶
¶ ¶ this is ¶ ¶
¶ ¶ ¶ ¶
¶ ¶impossible¶ ¶
¶ ¶----------¶ ¶
This would be a good time to type-in and RUN program 2. It does much the same thing on the computers as the above diagram does on paper. Notice how a single PLAYER assumes a number of different horizontal positions. This, of course, is all done with display list interrupts, but the structure of P/M graphics forces us to use a slightly different programming technique. The problem is that P/M graphics does not have shadow registers for the horizontal position of its PLAYERS and MISSILEs. Without any shadow registers to up-date the hardware registers, the horizontal position of a player cannot be reset when the computer completes a screen. After interrupt, the PLAYER column will simply move as a block to the location ordered by subroutine. No cutting of the PLAYER column will occur. Fortunately, there is a way around this problem. It may not be as simple as program 1, but it works. Because there is no shadow register to up-date the hardware register, we must do it ourselves. This means at least 2 interrupts, the first one to establish a new horizontal position and the second to reset it before a new screen is drawn. Program 2 is a good example of this technique. The first mode byte that interrupts the screen display is located a DLIST + 25. Notice that the Interrupt pointers direct the computer to the address of the first subroutine, $600 HEX (see below)
180 POKE 512,0:POKE 513,6
It is at the first subroutine where the horizontal position of the PLAYER column is moved further left. In order for the lower section of the PLAYER column to be moved back to its original position, a second subroutine must be created. Because of this, the first subroutine must change the Interrupt pointers so that they direct the computer to the address of the second subroutine. ($650 HEX) When the computer encounters the second modified mode byte (line 25), it will jump to the second subroutine. This routine (see line 110), moves the lower section of the PLAYER column back to its original horizontal position. So that the process can be repeated, the second subroutine changes interrupt pointers to direct the computer back to the address of the first subroutine ($600 HEX). Below is a diagram of how program 2 is splitting the PLAYER column.
¶ ¶ <= DLIST+125 INTERRUPT
¶ ¶ <= DLIST+125 INTERRUPT
Program 3 is a more finished example of the use of interrupts to split-up PLAYERs. After RUNing this program you will notice 4 figures with significant differences. Although all these figures were created from the PLAYER0 column, they vary in position on the screen, and in colour and widths. The horizontal positioning was accomplished by using the same method as in program 2. The colouring system is nearly identical to program 1. The only difference this time is that instead of changing the background colour, I changed the colour for PLAYER0. What is new this time is the difference in widths between the figures. As you probably already know, the P/M graphics system contains a group of bytes which control the width of the players. (see below)
|player?||address (dec)||address (hex)|
By using the interrupt subroutine to change a width byte during the time the computer is drawing the screen, multiple widths for one PLAYER column are possible. Like the bytes that control the horizontal position of PLAYERS, the width bytes do not have any shadow registers. To solve this minor problem I made the same duel interrupt system used to horizontally position figures in program 2.
The last application this article contains uses display list interrupts to present a number of different character sets on the screen at the same time. Many magazines have written a number of articles on character graphics. (see ROM 4 & 6) But most of these articles imply that the only way to have normal characters on the screen at the same time as modified characters was simply not to alter some. Well, there is an alternative method which could be more appropriate in programs that use a large number of normal and modified characters. As you might guess, this alternative method uses display list interrupts. Location 756 (dec) is known as the Character Base Register (CBR). Being the shadow register for the Character Base Address (54281 dec), this CBR points to the starting position of the character set that is currently displayed. When the computer is using the normal uppercase set, the Character Base Register contains a value of 224. This number is the location in pages (groups of 256 bytes) of the ROM character set. When you create a new modified set, the character base register must point to it before the computer will use that set. Because the character set pointer has a shadow register, a duel subroutine system is not needed.
Program 4 first creates a new modified character set and stores it in the memory. Then character base register (756 dec) is made to point at this is modified set. As a result, it is contained in the top half of the screen. When the Antic chip encounters the interrupt mode byte at the middle of the screen, the computer will jump to the subroutine where the character base register is made to point at the normal set. As a result, the characters at the bottom of the screen appear to be normal. Below is a example of what is meant.
¶ top ¶ <= new character set
¶ bottom ¶ <= normal character set
The following group of programs are examples of the material covered in this article. If you are still uncertain of the techniques used by the various applications of display list interrupts, examine the methods used in these programs.
2 REM ** PROGRAM 1 **
5 REM ** DISPLAY LIST COLOUR DEMONSTRATION **
12 REM * LOAD INTERRUPT ROUTINE
20 POKE DL+15,2+128:REM * IR MODE BYTE+128 *
30 FOR X=0 TO 14
40 READ D
50 POKE 1536+X,D
60 NEXT X
65 REM * INTERRUPT ROUTINE
70 DATA 72,138,72,169,70,141,10,212
80 DATA 141,24,208,104,170,104,64
85 REM * SET INTERRUPT POINTERS
90 POKE 512,0:POKE 513,6
95 REM * ALLOW INTERRUPT
100 POKE 54286,192
2 REM ** PROGRAM 2 **
4 REM ** CUT-UP PLAYER0 **
5 GRAPHICS 8
7 GOTO 1000
19 REM * FIRST INTERRUPT MODE BYTE *
20 POKE DLIST+25,15+128
24 REM * SECOND INTERRUPT MODE BYTE *
25 POKE DLIST+125,15+128
50 FOR I=0 TO 19
55 READ A:POKE 1536+I,A:NEXT I
60 REM * FIRST SUBROUTINE *
65 DATA 72,138,72,169,80
70 DATA 141,10,212
75 DATA 141,0,208,169,50,141,0,2,104,170,104,64
100 FOR I=0 TO 19
105 READ A:POKE 1586+I,A:NEXT I
109 REM * SECOND SUBROUTINE *
110 DATA 72,138,72,169,140,141,10,212,141,0,208,169,0,141,0,2,104,170,104,64
799 REM * INTERRUPT POINTER *
800 POKE 512,0:POKE 513,6
809 REM * ENABLE INTERRUPT *
810 POKE 54286,192
999 REM * P/M GRAPHICS *
1000 POKE 559,62
1010 POKE 53248,120
1020 POKE 704,88
1040 POKE 53277,3
1050 POKE 54279,1
1070 FOR X1=0 TO 255
1080 POKE J1+X1,255
1090 NEXT X1
2000 GOTO 10
5 REM ** PROGRAM 3 **
7 REM ** P/M APPLICATION **
10 GRAPHICS 8
12 COLOR 1:SETCOLOR 2,16,1
14 POKE 755,0
15 FOR X=1 TO 20:XP=INT(RND(0)*319)+1:YP=INT(RND(0)*150)+1:PLOT XP,YP:NEXT X
18 REM * SET UP P/M GRAPHICS *
20 POKE 559,62
30 POKE 53248,120
40 POKE 704,90
60 POKE 53277,3
70 POKE 54279,1
120 FOR X=1 TO 4
121 READ A
125 FOR X1=1 TO 8:READ D:POKE J1+A+X1,D:NEXT X1
128 NEXT X
130 DATA 155,129,129,219,255,255,219,129,129
132 DATA 180,0,1,3,63,255,36,0,0
140 DATA 100,57,42,60,120,80,120,40,108
145 DATA 75,129,66,60,60,60,66,129,0
1000 REM * INTERRUPT ROUTINE *
1120 POKE DLIST+65,15+128:REM * IR MODE BYTE+128 *
1125 POKE DLIST+125,15+128:REM IR MODE BYTE+128
1130 FOR I=0 TO 29
1140 READ A:POKE 1536+I,A:NEXT I
1150 DATA 72,138,72,169,90
1160 DATA 141,10,212
1170 DATA 141,0,208,169,148,141,18,208,169,1,141,8,208,169,50,141,0,2,104,170,104,64
1400 FOR I=0 TO 22
1405 READ A:POKE 1586+I,A:NEXT I
1410 DATA 72,138,72,169,155,141,10,212,141,0,208,169,0,141,0,2,141,8,208,104,170,104,64
1480 POKE 512,50:POKE 513,6
1490 POKE 54286,192
1500 ? " All created with PLAYER 0 (p/m) "
1510 ? "Different SIZES,HORZ.POSITIONS and COLORS"
1515 POKE 755,0
1520 GOTO 1520
2 REM ** PROGRAM 4 **
7 REM ** MULTI CHARACTER SETS **
8 REM ** CREATE NEW CHARACTER SET **
40 POKE 106,PEEK(106)-5
50 GRAPHICS 0
60 ? " Please Wait"
61 POSITION 8,4:? "Moving the character set"
62 POKE 755,1
70 FOR X=1 TO 1024
80 POKE NSET+X-1,PEEK(ROMSET+X-1)
90 NEXT X
100 POKE 756,NSET/256
120 FOR X=1 TO 10
130 READ LD
150 FOR X2=0 TO 7
160 READ D
170 POKE SET+X2,D
180 NEXT X2
190 NEXT X
498 REM * CHARACTER DATA *
499 REM * SHIP *
500 DATA 33,0,0,32,36,36,36,255,126
509 REM * ISLAND *
510 DATA 34,28,42,41,8,8,8,126,255
519 REM * ANCHOR *
520 DATA 35,16,254,16,16,16,146,146,124
529 REM * FLAG *
530 DATA 36,248,143,130,241,143,128,128,128
539 REM * BOTTLE *
540 DATA 37,0,0,248,135,129,135,248,0
549 REM * DOCK *
550 DATA 38,1,1,151,241,149,151,147,255
559 REM * CANNON *
560 DATA 39,0,1,127,127,49,120,180,0
569 REM * SEA GULL (up) *
570 DATA 40,0,0,195,60,24,0,0,0
579 REM * SEA GULL (down) *
580 DATA 41,0,0,0,24,102,129,0,0
589 REM * SMALL SHIP *
590 DATA 42,8,40,10,72,9,200,63,30
1118 REM * CAUSE INTERRUPT *
1120 POKE DL+15,128+2:REM * IR MODE BYTE+128 *
1130 FOR X=0 TO 24
1140 READ D
1150 POKE 1536+X,D
1160 NEXT X
1162 REM * INTERRUPT ROUTINE *
1170 DATA 72,138,72,169,88,141,10,212,141,23,208,169,50,141,24,208
1180 DATA 169,224,141,9,212,104,170,104,64
1190 POKE 512,0:POKE 513,6
1200 POKE 54286,192
1980 REM ** DRAW DISPLAY **
1990 POSITION 7,4:? "
2000 POSITION 5,0:? " ":POSITION 2,2:? "A":POSITION 5,6:? "A":POSITION 10,3:? "C"
2019 REM * PRINT CHARACTERS *
2020 FOR X=1 TO l0:POSITION X*3+2,10:? CHR$(64+X):NEXT X
2040 FOR X=1 TO 10:POSITION X*3+2,11:? CHR$(64+X):NEXT X
2050 POSITION 2,13:? "The top character is the modified version of the lower character"
2060 POSITION 2,18:? "By using DISPLAY LIST INTERRUPTS one is able to use both ROM and modified characters";
2070 ? " on the same screen"
2080 FOR X=5 TO 15:POSITION X,3:? "B":NEXT X
2090 FOR X=10 TO 14:POSITION X,5:? "B":NEXT X
2100 FOR X=20 TO 27:POSITION X,4:? "B":NEXT X
2110 POSITION 15,7:? "A":POSITION 20,3:? "A":POSITION 35,5:? "A":POSITION 27,2:? "A":POSITION 4,1:? "A":POKE 10,2:? "A"
2120 POSITION 2,2:? "C":POSITION 39,4:? "D":POSITION 20,5:? "E":POSITION 7,6:? "F"
2130 POSITION 5,5:? "G":POSITION 10,6:? "H":POSITION 25,3:? "H":POSITION 30,5:? "I":POSITION 37,3:? "I"
2140 POSITION 10,6:? "J":POSITION 34,4:? "J"
2150 POSITION 30,4:? "H":POSITION 22,2:? "I"
2160 POSITION 25,7:? "I":POSITION 32,6:? "H"
2170 POSITION 18,2:? "H":POSITION 21,7:? "H"
3000 POSITION 1,20
2 REM * CHECK DATA *
4 REM * FOR PROGRAM 3 *
5 DATA 10613,543,790,965,749,876,858,994,931,74,926,100,983,9,989,74,716,36
128 DATA 5455,781,404,766,110,997,87,128,80,130,163,73,507,185,338,156,82,468
1480 DATA 3843,668,193,115,956,979,932
1 REM * CHECK DATA FOR PROGRAM 4 *
2 DATA 8103,701,165,390,82,276,168,440,961,907,638,880,177,282,731,391,119,795
140 DATA 6658,232,129,722,203,829,780,727,69,925,196,892,197,136,37,336,214,34
549 DATA 10240,46,135,203,978,658,712,870,756,478,878,888,916,783,173,769,171,826
1162 DATA 9915,96,468,279,613,182,757,487,584,964,527,530,655,795,283,869,915,911
2110 DATA 3778,159,56,328,750,743,756,752,234