Celebration
Celebration was written to mark the 35th anniversary of the release of the ZX81. It uses pseudo high resolution techniques to generate graphic effects not seen before on a standard 16K ZX81.
The program demonstrates 3 new graphic effects:
- Generation and mixing of lower case letters with standard ZX81 upper case letters.
- Vertical scrolling of ZX81 characters at a pixel resolution.
- Generation of pixel perfect animated sprite characters.
The mixing of lower case letters with standard upper case letters also appears in the title screen of Against The Elements. The effect is more prominently used in Celebration and showcases a wider range of lower case letters.
The title screen and main display screen of Celebration are shown below:
The program includes full colour support for the Chroma SCART interface. The colour facilities should be switched on prior to loading the game.
The program generates these effects using a new pseudo hi-res display driver that allows access to a wider range of pixel patterns than is available in conventional pseudo hi-res programs.
Pixel Perfect Sprites
Games such as Software Farm's Forty Niner and Lothlorien's Micro Mouse Goes Debugging generate a hi-res display by changing the value of the Z80's I register so that the pixel patterns (i.e. group of 8 horizontal pixels) shown for each character code are fetched from a different area of the ROM. The display driver forces a reset of the hardware line counter after every scan line so that the same set of pixel patterns is available for every display line (normally the counter would increment and so fetch the set of 8 pixel patterns for a character from consecutive ROM locations). This has the advantage that a program can scroll the display vertically at a pixel resolution. The technique was originally devised by Macronics in 1981, although their driver could only generate a reduced width screen. The games sold by Software Farm and Lothlorien use an alternate driver that is more compact and able to generate a 256 pixel wide screen. The drivers are near identical and so presumably one is based on the other.
Software Farm devised an alternate pseudo high resolution technique for the game Z-Xtricator. The display driver for this game uses the standard pixel patterns in the ROM and allows the hardware line counter to increment after every scan line. However, unlike the ROM display routine it does not retrieve the same display file byte for the 8 lines of each character, but instead fetches unqiue bytes for each line from a larger display file. This allows the 8 pixel patterns for each line within a row to be composed from different character codes. Since the pixel patterns forming the Sinclair character set tend to be symmetrical in nature or formed from contiguous line segments, the characters that can be constructed have a more designed appearance. This is why the aliens in Z-Xtricator look perfectly formed compared to the sprites in Forty Niner, Rocket Man, etc. However, the technique has the disadvantage that the display can only be scrolled vertically at a row resolution (i.e. 8 pixels).
A true high resolution display requires the ability to display any of the 256 possible pixel patterns at every display position. The pseudo hi-res drivers above effectively translate each character code into a pixel pattern to represent it, but the ZX81 hardware is only able to support 128 character codes (0 to 63, and 128 to 191), with codes 128 to 191 (i.e. with 7 set) simply resulting in inverted versions of the pixel patterns for the corresponding code where bit 7 is reset. The value of the I register dictates where in the ROM the pixel patterns will be fetched from, but since these locations contain instructions and not purposely planned pixel patterns it is not guaranteed that every character code will equate to a unique pixel pattern. The number of unique pixel patterns varies for each value of the I register (which for hardware reasons can only be even values between 0 and 30), and at most it is only possible to achieve in the region of 100 unique pixel patterns.
The choice of value for the I register determines the pixel patterns available. If a particular pattern is not available using one value of I register then it might be if another value is selected. The problem is that any patterns previous found using the original value might no longer be available using the new value. The solution that the display driver in Celebration uses is to allow the value of the I register to be changed after every scan line. In addition to this, it allows the hardware line counter to be selectively reset or to incremented after every scan line. Therefore if a required pixel pattern can not be found when the line counter has a value of 0 then perhaps it can be found when the line counter has a value of 1. Or perhaps it can be found if the line counter has a value of 2, so long as the pattern for the previous line is available when the line counter has a value of 1, and the one before it when the line counter has a value of 0. And so on.
These two mechanisms allow the display driver in Celebration to show sprites composed of pixel patterns obtained from a much wider I-space and hence able to achieve perfect replication of a few well known Spectrum game characters.
The technique has the disadvantage that vertical pixel level scrolling of a sprite is not possible. Another limitation is the size of sprites that can be animated. If the value of the I register must be changed for a particular scan line to render the next frame of a sprite then it will be necessary to replace all character codes within the scan line so that identical pixel patterns are produced using the new value of the I register. All codes must be replaced before the start of the next TV frame to avoid 'old' character codes being rendered using the new value of I register and so appearing to momentarily become corrupt. The size of sprite that can be animated largely depends on the number of changes of I register that are required per frame. A third limitation is that the pixel patterns obtained using a value of $02, $04, $06, $0E or higher for the I register are different between the edition 1, edition 2 and edition 3 ZX81 ROMs. To support all ROMs a program must either avoid these values, only use the pixel pattern values that exist across all 3 ROM variants or define a different set of sprite bitmaps specific to each ROM.
Lower Case Lettering
The lower case lettering is achieved using the pseudo hi-res technique employed by Z-Xtricator. By setting the character codes for each line within a row to the same value, the 8 pixel patterns for a standard ROM character bitmap are fetched and a normal upper case character is displayed. To construct a lower case letter, the codes for each line within the row are set to different values and results in pixel patterns being fetched from different character bitmaps. Lower case and upper case lettering have fairly similar characteristics and so the patterns that make up the upper case letters are well suited for constructing lower case letters, which results in a font that looks almost flawless. The technique also allows upper and lower case letters to be freely mixed within a row.
Z-Xtricator did not use this pseudo hi-res technique to display lower case letters but instead used it to create some very neatly formed aliens. However, this aspect has largely been overlooked and instead the game is generally remembered as a disappointment compared to Forty Niner and Rocketman.
Using the technique it is possible for a ZX81 program to display the full ASCII character set, as shown below:
The technique used by Forty Niner (and almost all other pseudo hi-res programs) also allows a ZX81 program to display the full ASCII character set, but the results look less elegant. For example here is a set constructed using a value of 8 for the I register:
Vertical Pixel Resolution Scrolling
The upper case lettering that scrolls up in the title screen of Celebration is constructed using the pseudo hi-res technique employed by Z-Xtricator. The hardware line counter is reset at the start of the scroll area and then allowed to increment after every scan line. Upon the next frame, the hardware line counter is reset one scan line sooner than on the previous frame and all bytes within the scroll area copied up one line. This results in the start of each row occurring one scan line higher up than it did on the previous frame. Once the text area has been scrolled up 7 times, the hardware line counter can once again be reset in line with the top of the scroll area.
Lightning Display Driver
The Lightning display driver provides the benefit of allowing the construction of a display using any mixture of modes, switching back and forth between them as desired. However, this comes at the price of requiring more bytes per display line than the conventional pseudo hi-res drivers.
The Lightning display driver supports 3 modes of output:
- Reset Line Counter mode.
- Incrementing Line Counter mode.
- Row mode.
In Reset Line Counter mode, the driver forces the hardware line counter to reset prior to outputting the characters of the line. The driver then advances to the next line of the display file. An example of a display file consisting solely of lines rendered using Reset Line Counter mode is the pseudo hi-res screen output by Forty Niner, where an I register value of $0C is used for all lines.
In Incrementing Line Counter mode, the driver allows the hardware line counter to increment prior to outputting the characters of the line. The driver then advances to the next line of the display file. An example of a display file consisting solely of lines rendered using Incrementing Line Counter mode is the pseudo hi-res screen output by Z-Xtricator, where an I register vaue of $1E is used for all lines.
In Row mode, the driver allows the hardware line counter to increment prior to outputting the characters of the line. The driver then repeats the same line of the display file a further 7 times before advancing to the next line of the display file. An example of a display file consisting solely of Row mode is the standard Sinclair display output, which uses an I register value of $1E for all lines.
The Lightning driver allows a display file consisting of any combination of the above modes. Mixed mode displays output by games such as War Web from Pooter Games can easily be constructed using the Lightning driver, and have the benefit of requiring less RAM for the rows of Sinclair character text. It is simpler and faster for a program to alter the displayed text, and avoids having to define an alternate character set using pseudo hi-res graphics.
Display Generation
Each line of the display file consists of the following fields:
- A optional byte specifying the value to set the I register to.
- The character code bytes forming the line of the display.
- A JP instruction that returns control to the display driver after the line has been rendered.
The display driver read the first byte (if present) and sets the I register with it. It then jumps to 'execute' the character codes forming the line of the display. The Z80 fetches a character code from the display file thinking it is an instruction. The ZX81 hardware intercepts the byte read and feeds the Z80 a NOP (No OPeration) instruction instead. The Z80 executes the NOP instruction, which as its name suggests does not change anything. As the Z80 is processing an instruction, it performs an automatic memory refresh cycle which is intended to top up the charges used by dynamic RAM to hold their contents. The ZX81 hardware repurposes the refresh cycle to use it to look up the pixel pattern from the ROM for the character code just read from the display file. This mechanism repeats until the Z80 fetches the JP instruction at the end of the line in the display file. The JP instruction opcode has bit 6 set, which the ZX81 hardware will detect and interpret as meaning it should not substitute the byte for a NOP instruction. The Z80 therefore executes the jump instruction and so returns control to the display driver routine. It is the address jumped to that determines the type of line the driver outputs next.
The display file ends with a RET instruction. The RET instruction has opcode $C9, which has bit 6 set, and so it is passed through to the Z80 for execution rather than being subsituted for a NOP instruction. For Reset Line Counter and Incrementing Line Counter modes the final line's JP instruction can be replaced with a RET, but for Row mode it must still end with a JP instruction instruction since the same line must get re-executed a further 7 times. This means that when ending the display file with Row mode, it is necessary to include a final dummy line consisting of just a RET instruction.
The driver contains routines to support 6 types of output:
- Output 1 line in Reset Line Counter mode using a new I value.
- Output 1 line in Reset Line Counter mode using the existing I value.
- Output 1 line in Incrementing Line Counter mode using a new I value.
- Output 1 line in Incrementing Line Counter mode using the existing I value.
- Output 8 lines in Row mode using a new I value.
- Output 8 lines in Row mode using the existing I value.
The driver can either delegate generation of the VSync pulse to the ZX81 ROM or can implement its own VSync routine. The former avoids a program needing its own code to replicate the activities of the ROM VSync routine, but it offers the advantage of allowing custom activities to be performed instead, e.g. reading only those control keys required by the program or including support for the ZXpand joystick interface, etc.
If fewer than 32 columns are required then alternate line output routines could be created to handle the reduced line length. This would have the benefit of minimising the memory required for the display file. It is even possible to create a display file with a 'staggered shape' where not all lines contain the same number of characters, similar to a collapsed standard display file.
It is even possible to change the value of the I register part way through a line by inserting the Z80 instructions ADD A,nn and LD I,A. These have opcodes $C6 and $ED respectively and are executed directly because bit 6 is set in each case. The A register will always hold the last value loaded into the I register and so these instructions cause an offset of nn to be applied to the I register. While the two instructions are being executed the ZX81 hardware will force the video output to white. It takes 16 T-states to execute the two instructions and because each clock cycle corresponds to 2 pixels this results in a white bar 4 columns wide appearing. The instructions are formed from 4 bytes and so neatly replace the 4 bytes that would have specified characters to display. Therefore the number of bytes defining the line within the display file does not change.
Finding Sprite Bitmap Codes
To assist with searching through I-space to determine the availability of pixel patterns required for the various famous Spectrum game characters, I developed a Windows application that systematically tries out all combinations. The utility allows an image to be drawn using the left and right mouse buttons to set and reset pixels respectively, or to paste in an image from the clipboard. A range of options allows the search to be narrowed down so that only codes applicable to 'Macronics' mode or 'Z-Xtricator' mode are located, and whether to target the ZX80 or the three ROM editions of the ZX81. A screenshot of the utility is shown below.
By default all values of I register and all possible line counter permutations will be searched. The search occurs from the lowest I register value upwards using a line counter value of 0. Only if match is not found is the line counter incremented. Options are provided to search only a specific value of I register, and to force the line counter to remain at 0 or to always increment.
Underneath the sprite drawing area are two rows of buttons used to provide basic manipulation controls. If all pixel patterns can not be matched then shifting the sprite left or right will result in a different set of pixel patterns that might match instead. Sprites can be saved to a file in a simple binary format and subsequently reloaded.
The Inverted option selects whether the background colour is white (unchecked) or black (checked). When unchecked, the left mouse button sets a pixel and the right mouse button resets it. When checked, the functions of the mouse buttons are swapped over.
Clicking the Find Codes button will begin the search through I-space, begining with the top line and working downwards. The utility seeks a match on each column entry
within a line using the same values for the line counter and I register. The utility is a single threaded application and so will be unresponsive
while the search is in progress (which can take several seconds). The results will be displayed in the four columns to the right of the sprite area,
with each entry shown in the following format:
Line Number = Line Counter : I Register : Character Code
The results can be written to an assembler text file by clicking the Create ASM button. Each line of entries will be written to the file as a DEFB statement which specifies the line counter value, the I register value and the four character code values.
The pixel patterns available for the ZX81 varies significantly between the editions of the ZX81 ROM for the higher values of I register. The utility can search for pixel pattern matches in a specific edition ROM or through those values that are common between the three ROMs.
The Show Patterns button will replace the sprite area with a display of all pixel patterns available using the currently selected options for the I register and line counter values. The first two columns of the sprite area show the pixel patterns for character codes $00-$3F and the second two columns show the inverted versions of these. If the ROM Selection option is set to All Editions of the ZX81 ROM then matching pixel patterns might not be available. In such cases the normal and inverted patterns for a character will be both shown as all pink pixels. The bottom left corner of the utility contains a status area that shows the number of unique patterns available for the current selection. If the mouse pointer is moved over the pattern data then this status area will show the character code and the numeric value of the pixel pattern. An example of the pixel patterns mode is shown below.
As described previously, to animate a sprite it is desirable that specific lines use the same I-space configuration between frames. The ZX Sprite Designer does not provide a means to cross-reference the results for different sprites. This is a feature that might be introduced in the future if there is sufficient demand for it.
Downloads
The program can be downloaded below, and is made available for private use or for public demonstrations. It is not to be included in emulator distributions, sold as part of a commercial package or replicated on another website (please do not re-distribute the program file but link to this site instead). Should you like it and wish to make a small payment for it then any amount would be appreciated and can be done using the Donate link at the bottom right.
Click here to download Celebration in .P and .P81 program formats (D8101). [Version 1.01] |
Click here to download the source code and demonstration program for the Lightning Display Driver. [Version 1.02] |
Click here to download the ZX Sprite Designer. [Version 1.2.0] |
Click here to download the bitmap definitions for both pseudo hi-res ASCII character sets ('Macronics' and 'Z-Xtricator' modes). |
The ZX81 program filename is CELEBRATION.
The program can be used with ZX81s configured for either 50Hz or 60Hz but note that the program is hard-coded to always output a picture at 50Hz. This is because a 60Hz picture contains fewer border lines and does not provide enough time to update the sprites between TV frames.
Celebration was released on 17th March 2016, with an update to include Chroma colour support released on 30th March 2022.