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 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 generates these effects using a new 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 $0E or higher for the I register are different between the edition 1 and edition 2 ZX81 ROMs. To support both ROMs a program must either avoid these values 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.
New Display Driver
The display driver devised for Celebration uses a display file in which each line consists of the following fields:
- A single byte specifying the value of the I register to use for the scan line.
- 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.
For each line of the display file, the display driver read the first byte 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 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. This 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.
The address to return control to is defined by the two bytes that follow the JP opcode byte within the display file. If the hardware line counter must be at 0 for the next scan line then the address specified is to a routine that forces the counter to reset. If the hardware line counter is to increment for the next scan line then the address is specified to a different routine that does not force the counter to reset. In this way, a program can selectively cause the hardware line counter to reset or increment after each scan line.
A pseudo hi-res style screen as displayed by Forty Niner can be created by setting the I register field for every scan line to hold the same value, and by specifying the jump address at the end of every scan line to go to the routine that forces the hardware line counter to reset.
A pseudo hi-res style screen as displayed by Z-Xtricator can be created by setting the I register field for every scan line to hold the same value (i.e. $1E), and by specifying the jump address at the end of every scan line to go to the routine that does not force the hardware line counter to reset.
The display driver can also replicate the standard Sinclair display mechanism by setting the I register field for every scan line to hold the same value (i.e. $1E), and by specifying the jump address at the end of every scan line to go to a routine that executes the bytes in a display file line 8 times before advancing to the next line. The routine would allow the hardware line counter to increment so that the correct pixel patterns are read for each line within the row. This option is useful to allow the construction of a mixed-mode display, similar to that used in the 1984 port by Ales Martinik of Manic Miner. A mixed mode display has the benefit that fewer bytes are required in the display file to show a row of text, and it is easier and quicker for a program to make changes to the text.
Variations of end-of-line handler routines described above can be used to allow a different number of columns per line or row, from 0 columns to 33 columns. If the value of the I register does not need to be changed very often then it can be omitted from the start of each line / row and a different end-of-line handler routines used instead that does not attempt to set the I register. These variations allow a program to minimise the memory used for the display file.
The final line of the display file ends with a RET instruction instead of a JP instruction and this returns control back to the driver to generate the bottom border area. 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.
It is also 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.
The new driver generates a VSync pulse of 4 scanlines duration, which is 2 shorter than the ZX81 ROM display driver generates and brings it closer in line with the standard video format. The bulk of the VSync generation routine is simply a delay and so this can be replaced with fixed duration routines to perform activities such as reading the keyboard, reading a joystick, incrementing a frames counter, etc.
The 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 disadvantage of requiring more bytes per display line than the conventional pseudo hi-res drivers.
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 two 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 two 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 two 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 both Edition 1 and 2 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 white 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.
|Click here to download Celebration in .P and .P81 program formats (D8101). [Version 1.00]|
|Click here to download the source code for the Lightning Driver. [Version 1.00]|
|Click here to download the ZX Sprite Designer. [Version 1.1.0]|
|Click here to download the bitmap definitions for a full pseudo hi-res ASCII character ('Macronics' and 'Z-Xtricator' modes).|
The ZX81 program filename is CELEBRATION. If a ZXpand interface is connected then its ROM must be disabled in order for the program to function correctly. This can be achieved by renaming the .p file as celeb.p (since the file name must be no longer than 8 characters) and then loading it using LOAD "CELEB;X".
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.