New approach to ATARI graphics
by Thomas McNameeThe title of this article may be a bit confusing - is it about Forth or assembly language? Well, it's about both. This article demonstrates a powerful feature of Forth: any language, new or existing, can be written in it. Assemblers written in Forth combine the power of Forth with the speed of machine code. Because assembly is taking place under the Forth system control, macro capability and complex arithmetical calculations are available during compilation. Some features of the ATARI, such as display list interrupts and vertical blank routines, can be written only in 6502 machine code; these are the applications addressed in this article. But first we will cover some of the essentials of Forth assembler.
THE FORTH ASSEMBLERBecause Forth is a stack-based language, it appears to be written "backwards". The same is true for the Forth assembler. Here is a comparison of some traditional instructions versus Forth:
|LDA 712||712 LDA,|
|STA COUNTX||COUNT ,X STA,|
Read the INX, instruction as "compile the opcode for the Increment X instruction." Note that Forth instructions require the operand to precede the operation. In the second example above, COUNT has been defined previously as a constant, so that its address appears on the stack before the indexed store command is compiled. The display list example contains a standard ATARI assembler listing which is the equivalent to the Forth word GR7DLI; it demonstrates the "backwards" instruction coding and a comparison of control structures.
In traditional assemblers, the instructions which control program execution are limited to Branch and jump group of insructions. Since there is no label field in Forth assemblers, jumping around inside a definition is not possible. In making up for the lack of a label field, the Forth assembler allows for many more ways to control program flow. The IF..ENDIF and IF..ELSE..ENDIF can be used to control execution based on the tested status of the Sign, Overflow, Zero, and Carry flags. Operation of these words is identical to the same structure in Forth. Other Forth control structures which are repeated in the Forth assembler include BEGIN..UNTIL, BEGIN..WHILE..REPEAT, and BEGIN..AGAIN. These combine to create a powerful set of control structures which greatly simplify programming.
You must know the rules of parameter passing to use the Forth assembler. Follow these rules whenever you use an assembly language definition along with other Forth words. The most important rule is that the X register must be preserved. It is the pointer to the top of the parameter stack and should be manipulated only when changes to the stack are desired. The second rule is that every assembly language definition must end with an appropriate re-entry jump; these jumps link the assembler definition with the rest of Forth. Of course, if a subroutine is being written, it may end in RTS. Two of the examples in this article end in other ways. However, these words are never executed within Forth, as their return statements would cause the ATARI to lock up. Selection of the appropriate re-entry point will depend on your requirements for the stack; these points are fully described in the assembler documentation.
VERTICAL BLANK ROUTINESScreens 10 through 13 are the words needed to install and test a vertical blank routine. The first screen contains the word SUBROUTINE, which is supplied in the ValFORTH documentation. This is a defining word which returns its address when executed; it also invokes the ASSEMBLER vocabulary. It is used on both the vertical blank routine and the display list interrupt, and it passes its address to the Forth word responsible for installing the routine.
The definition of INSTALL is a good example of a traditional Forth assembler word. Note that INSTALL begins with CODE instead of ':' and ends with a C; instead of ';'. These are defining words which switch in the ASSEMBLER vocabulary while the definition is being compiled. The word INSTALL is designed to synchronously set the deferred vertical blank vector; this prevents the vertical blank routine from occurring while the new vector is being set up. INSTALL can also be used to set vectors for system timers 1-S and the immediate vertical blank by changing the #7 in line 10 of screen 11 as follows:
|Timer 1-5||# 1-5|
|Imm. VBLANK||# 6|
Screen 12 contains the world VBLANK, which turns the routine on and off, and uses INSTALL to insert the address in the appropriate subroutine. Screen 13 contains the vertical blank subroutine, a simple word which increments the background color in any graphics mode. TEST is the word which brings it all together, installing the address and diverting the vertical blank flow through the subroutine ROTBAK. Note that ROTBAK ends with JVB JMP,. JVB is the re-entry point for the vertical blank routine and is defined as a constant on screen 11. All vertical blank routines must end this way.
To use, LOAD screens 10-13 and type TEST. The background color should begin to change. To turn off this routine, type OFF VBLANK. It is important to remember to turn off vertical blank routines before doing anything to the Forth dictionary. If you FORGET all or part of your routine, the vertical blank vector will be pointing at nothing, and the machine will lock up.
DISPLAY LIST INTERRUPTSThis gets a little more spectacular. Screens 20 through 23 contain the installation word, the display list modifier, and a test word. Screen 20 is the display list equivalent of the INSTALL word used in vertical blanks. DLI installs the subroutine address and enables display list interrupts. DLIMOD, on screen 21, sets the interrupt bit on each Graphics 7 instruction in the display list. Screen 22 is the actual interrupt routine.
GR7DLI increments the variable COUNTR each time the subroutine is called. Since there are 79 interrupts in our modified display list, COUNTR is reset to zero after 79 (hex 4F) executions. COUNTR is used in the X register as an index into COLTAB (color table) which is really page 2. The color selection in this example is therefore random, but COLTAB could easily point to a user-defined color table. GR7DLI is the Forth translation of the routine which appears on pages 5-6 of De Re Atari, which also contains an excellent discussion of display list interrupts and their application. Note that the subroutine GR7DLI ends with RTI, since it will be called as an interrupt by the operating system. 80COLORS is optimistically named, since many of the colors from our page two color table are black, but it does produce an interesting display when executed. Note that screen 22 uses SUBROUTINE, so screen 10 must be loaded first.
Accompanying this example is a listing of GR7DLI (Listing 2) written with the Atari Assembler Editor. Check this listing with screen 22 to see how the Forth assembler compares. It may be easier to learn the "backward" assembler using this as a basis for comparison.
COMBINING BOTH TECHNIQUESAfter loading screens 20-23, try screens 24 and 2S. RAINBOW is a routine which increments COUNTR, then makes it the background color. This makes every line on the screen a different color. After 79 interrupts, COUNTR is reset to the value of RESET. SPECTRUM installs RAINBOW and turns the DLI routine on. CYCLE is a vertical blank routine that either increments or decrements COUNTR based on the value of MVFLG. MOVECOLORS uses the constants UP or DOWN to determine the direction of color movement on the screen. To make the colors swim upwards, type UP MOVECOLORS; DOWN MOVECOLORS moves them down. This is not a scroll routine; try some PLOT and DRAWTO statements and note that the drawn figure stays stationary while the background colors move.
The Forth assembler can be used to code critical routines for speed, and it can be used for machine language routines such as those described above. For further study in this area, I recommend ValFORTH's 6SO2 Macro Assembler, and De Re Atari, available from APX.