CHARMODE DEMO An Amiga Character mapped display mode generated by Custom Chips FEATURES Same-field update of the entire screen with beam avoidance Support for up to 640 x 287 resolution (currently PAL only) font support for up to 256 characters each up to 16 pixels tall Support for dynamic font changes from one field to the next Remaps screens encoded as one byte per character into Amiga bitplane graphics. This demo uses the copper to generate a custom screen mode and convert character codes into graphic patterns from a font, by programming the blitter. All processing is performed by the custom chips. All the CPU need do is write a small table of character codes to determine the new display. Almost all the main CPU time is available for other purposes. This document is edited from the 'internal' development documentation, and should be read in conjunction with the fuller explanations in Amiga Format. CharMode is intended for use in emulators for vintage computers and terminals with character-mapped displays. These include TRS-80 (64 x 16 characters of 8 x 12 pixels, on a 512 x 192 pixel grid), CP/M, IBM MDA and most ASCII terminals (typically 80 x 24 or 80 x 25 characters), PET (40x25 8 x 8 characters), ZX80 and ZX81 (32 x 24 characters), MSX, Dragon, Atari, etc. It uses a font of 256 characters, each eight pixels wide and up to 16 pixels tall, with one byte, left justified, in each pattern WORD. Thus the font takes 256 (characters) x 32 (bytes) = 8K. If the character height is less than 16 pixels the rendered character is truncated at the bottom. Dynamic font changes are supported on a per-field basis by writing the changed data to the corresponding font words. These need to be unpacked as the font must use one WORD per character line (as it's a 16 bit blitter). Automatic unpacking of 8 x 8 fonts is implemented. Supported Screen formats: Char Width Char Height Chars/Row Chars/Col Prime Time Characters 8 8 80 24 58 1920 8 8 80 25 47 2000 8 8 80 28 17 2240 8 8 80 29 6 2320 8 10 80 24 14 1920 8 12 80 20 20 1600 8 16 80 16 8 1280 8 8 64 32 40 2048 8 8 64 35 15 2240 8 16 64 16 40 1024 8 12 64 16 52 1024 8 10 64 25 52 1600 Those formats are high resolution. Thsse are some low resolution formats: 8 8 40 24 106 960 8 8 40 25 97 1000 8 8 40 32 40 1280 8 10 40 24 62 960 8 10 40 25 52 1000 8 12 40 16 108 640 8 16 40 16 40 640 8 8 32 24 106 768 8 8 32 32 40 1024 8 10 32 24 62 768 8 12 32 20 60 640 8 16 32 16 40 512 These are all tested examples. Many other formats are possible; in general the code expects widths of 32, 40, 64 or 80 characters and from 16 to 32 lines. Other values may require additional ad hoc code to fine-tune display timing. Notes on the formats Char Heights are in Pixels. All character widths are eight pixels. Line widths must be even as we're using a 16 bit chip set. Prime Time is the number of scan lines of time available from the start of the field before the copper starts to blit character patterns. Normally this is padded by adding HeadLines - scan lines when the display is visible but not part of the blitted character map. 25 undisplayed scans are available at the start of each field. In practice one additional line time should be allowed for the copper initialisation at the start of each field. It's advantageous to allow more lines, but this moves the character mapped screen portion down the display. With 2000 8 x 8 pixel characters on-screen (25 lines of 80 columns) up to 87 lines of heading are supported (in PAL mode the line limit is 287, and 25 x 8 = 200 pixels for the character map) and this gives the latest allowable character map on the screen, with the maximum time for updates at the start of the field - around 5500 microseconds. High values of Prime Time indicate that the display could be moved higher up the screen (rather than extending to display line 287) while still allowing VBLANK updates. If the Prime Time value is low it may be necessary to start the CPU update as the blitter finishes updating the last character and the last line begins to be displayed - this boosts the available time from a near-impossible eight lines to 24 with 80 x 16 8 x 16 characters. Beam Avoidance is assured by timing the blits so that the last blit for the last line is complete before that row of characters starts to be displayed. It follows that character map or font updates can be performed at any time from the start of display of that line, rather than top of frame. This is more complicated than waiting for VBLANK - it requires a copper interrupt - but gives more time for interrupt response and update before the data is needed for the next field. These modes are expected to be used on machines with fast memory, as much of the chip RAM bus bandwidth is consumed by the copper and blitter updates. It makes sense to keep the master copy of the current screen in fast RAM. If the fast memory is more than twice the speed of chip RAM - as in the case of A3000 and A4000 systems, and expanded machines with processor-local 32 bit memory on the CPU card - it is generally worth keeping a second copy of the last screen written in fast RAM, and only writing changed information at the start of a field. A similar technique can be used for font updates, as it's rare that the entire font will change every field. Word or long word transfers are important to ensure quick enough updates from fast RAM to chip RAM. It may be worth adding a blitter pass to expand the font from byte to word format. The trade-off is the extra chip bandwidth required against the reduced time that the CPU spends waiting for writes to Chip RAM to be completed. HiRes character updates are performed with the BLITTER_NASTY bit set, consuming all available processor memory cycles as each character is generated, but the processor still gains access to memory at least four times per scan line as WAITs are interspersed between the blits. There's much more bandwidth available just before and after the vertical blank, and that is when writes to the font and character map should be performed. Not only does the processor get more time then, it's the ideal point at which to copy the emulated screen to the real screen hardware to minimise delay and ensure field synchronisation, with all characters changed in a given emulated field guaranteed to appear in the next display. The blitter and copper instructions used are compatible with all Amigas from the original OCS through ECS to AGA. 30 KHz VGA monitor support could be but is not included. Scan doubling would require lines to be generated twice as quickly. The current code is designed for a 15 KHz PAL display and would need changes for other modes. It was developed and tested with Amiga Qdos, and is intended to be used in custom screens on the Amiga, with ownership of the blitter throughout updates, only relinquishing it when the emulator screen is swapped off or paused. The code sets out to centre the display vertically and horizontally, while ensuring plenty of free time at the start of each field for updates from the CPU. In total, the display must be at least 192 pixels tall and start by scan 64 - well within limits of all Amiga chip sets. Extra lines can be added at the top of the display. These are not used for the character map but can be written (albeit slowly) by the processor and are ideal for headings, menus and emulator status. They are added at the top to give the blitter more time to generate the field without beam splitting. In order to leave some bandwidth for the CPU, even in the middle of the bitmap, high resolution 8 x 8 pixel character blits need slightly more blitter and copper DMA time than they take to be output, so generation must start earlier. For instance a display of the maximum number of characters (2320, in 80 columns and 29 rows) requires at least 50 display scan lines of head-start to ensure that all character lines are regenerated ahead of the beam. This comes close to the limit of 29 x 8 = 232 pixel lines of text and 55 for the HeadLines. No more than 30 line times are available at the start of the field for CPU screen updates - less than 2 milliseconds. That is not long, even when there's only about two and a quarter K that needs to be written to update the entire screen. Allow for interrupt latency (up to 4 lines) & remember that Chip RAM bandwidth available to the CPU may be as low as 1.8 Mb per second on many Amigas, even without DMA contention, so that a full screen character write can take up to 20 scan lines to be completed! At best, on Commodore 68030 or Phase Five accelerators, 5 lines are needed. As of version 0.13, the code was not optimised to work out the best wait between blits and ideal start line for LowRes characters, or HiRes characters more than 12 pixels high. It may finish blitting long before the last line, indicating that more time could be available between blits or (possibly) they could all start later. The ad-hoc code to work out Prime Time only attempts to maximise it for less tall characters, where the copper overhead for each blit is more significant and keeping up with the beam is problematic. COPPER LIST COMPILER The Copper list is generated by a SuperBASIC program, the Copper List Compiler. This contains instructions to do the following things 50 times per second: (1) Set up a custom bitplane display centred on the PAL screen (2) Expand an 8 x 8 pixel character font into internal format for blitting (3) Convert an 8 bit table of character codes into pattern blit instructions (4) Generate the new display by blitting one pattern for each character code The Copper list Compiler (CharMode_bas) also calculates and displays the number of the line on which blitting will start, so that the user can ensure CPU updates will occur in time. Extra lines can be allocated to ensure beam avoidance. It can include instructions to show the time taken for each activity by colour-coding the screen in stripes. The Compiler has been tested on systems ranging from AGA A4000/060 to ECS A500. All supporting software is PD. It uses SuperBASIC extensions, all of which are provided with the exception of Toolkit 2 or PD Toolkit. The BOOT program assumes that Toolkit 2 is available. Compilation parameters To generate a custom copper list for a given display format, change the assignment of these variables at the start of AutoChar_bas. ShowTime 0 or 1 Flag - set to get coloured stripes on screen Green: extracting even characters from the character map to the Copper List. Blue: extracting even characters from the character map to the Copper List. Red: ascending pass blitter unpack. CPU update can overlap if working forwards Purple: descending pass of blitter unpack - changes in this phase get displayed one field late - but blitter is not BUSY so CPU writes go at reasonable speed. Dark Blue: rendering characters by blitting patterns from the font. This area should end before the start of display of the last character line. Blitfont 0 or 1 Flag - expand 8 x 8 font to 8 x 16 automatically every field? This is too slow to be feasible with a full screen HighRes display, but can be performed on a partial-height screen if HeadLines are allocated to give time for the update at the start of each field. It only works with an 8 x 8 character font. BlitChars 0 or 1 Flag - include code to expand the CharMap? Otherwise to change a character you need to poke a word giving the base address of its pattern in the font into the copper list at a corresponding position. For instance with an 8 pixel tall font, to display character 65 (ASCII "A") you'd poke 65 x16 (bytes per character) into the copper list. Multiply by 32 for characters up to 16 pixels tall. The characters are controlled by words between the pointers FirstCharMove and LastCharMove (both absolute addresses). The first character's font pointer word is at FirstCharMove. The second is FOUR BYTES EARLIER. The third is 20 bytes on from the first, the fourth is at offset 16 from the start, and so on: FirstCharMove,FirstCharMove-4, 20, 16, 40, 36, ... LastCharMove+4,LastCharMove CharHeight% 1 to 16 Height of each character in pixels Typically 8, 9, 10, 12 or 16. fonts 8, 9 and 16 pixels high are included. The tallest uses IBM PC character codes, the shortest follows the MGT SAM convention with duplicates of the ASCII set 32-127 in the second half (ignoring bit 7) while the nine pixel high one is the 256 character Minerva Qdos set. CharWidth% 8 THIS CANNOT BE CHANGED! CharColumns% 32,40,64,80 Number of characters on each display line High Resolution (70 nS pixel) mode is used for widths over 40 characters (320 pixels) Other even values also work but may not be optimally timed. Odd values are not allowed, for reasons of 16 bit alignment. CharLines% 1 to 35 Number of character lines HeadLines% 0 to 279 Number of pixel lines in bitplane BEFORE characters start These is an interaction between these two. When a large number of CharLines% is chosen, the blitter needs more time to render them, so HeadLines% may be required to delay the display of characters until the blitter has had time to render them. This has the unfortunate effect of pushing the display down the screen, but does give some extra space for menus or headings at the top. There's no reason why the HeadLines should not be in a different mode, although the compiler assumes one bit map for the characters and heading. The important thing is to delay output of the characters to give time for CPU update of the CharMap and font, Copper and Blitter unpacking of those into more Copper and Blitter data, and ensure that rendering of the characters still precedes the beam! The compiled Copper list uses absolute addresses but can be relocated to any 64K boundary by adjusting the pointers to PAGE, set at or fairly near the start. The page alignment is vital because the font must be at the start of a page so that it can be efficiently indexed with scaled character codes from zero in the low word of blitter pointers. Compiler Limitations This section discusses the limits imposed by the choice of data structures and design of the Copper List Compiler. These are practical limits associated with the current implementation on OCS Amiga hardware, rather than theoretical limits of the CharMode principle. The current version of the compiler (0.13) only supports dynamic font unpacking for 8 x 8 character fonts. This suits all the machines (Atari, C64, MSX, Sinclair ZX-81) which require it. Taller fonts are supported but not dynamic. In other words modified bytes must be written unpacked (16 bits wide, left justified). The unpacking consumes quite a lot of blitter time - too much for full-screen HiRes display. It's fine for displays of up to about 1000 characters, and again that corresponds to the displays we'd want to emulate. The compiler assumes a 50 Hertz PAL display with up to 287 lines. NTSC and Double-scan VGA modes could be supported but with reduced performance as the display pushes out more pixels for a given amount of time available for blitter activity. Characters must be eight pixels wide. This is a consequence of the way the blitter is used to render two characters at once. Peter Corlett had this key idea, without which the code would be much slower as it would need to mask and generate one character at a time. It's easy to render 16-pixel wide characters at fair speed with simple un-masked blits, but in general other widths would be rendered much more slowly. These modes all use a single bitplane and two colours. This minimise Amiga Chip RAM and bandwidth requirements, and suits the old monochrome screens which were standard for character-mapped modes. Colour character-mapped modes could supported analogously but need a different approach as all output goes to a single bitplane. This can be re-coloured efficiently with a copper list, but the extra copper instructions to change colours must be fitted around the existing heavy copper activity. Attribute plus bitplane graphics (as on C64, MSX, SAM, Spectrum and TI-99/4) can be emulated similarly, although the heavy copper-blitting characteristic of character-mapped emulation is not needed in such cases. Simon N Goodwin, simon@studio.woden.com, January 2000 DATA STRUCTURES The CharMode data structures start on a 64K boundary in Chip memory. font and Bitplane data is guaranteed to fit in the first 64K, while the Copper list will start there but may extend into the next 64K page. font size: 8K (allocated - may not be all used) If LoRes 8 x 8 characters are in use the font is stored in two forms. At offset +4096 there is a 2K packed byte image, as would be used in an 8 bit micro, with 8 x 256 bytes per character. This is immediately preceded by a blitter-generated 4K version, identical but that each packed byte is followed by a zero byte for convenience when performing 16 bit blits. The packed data is followed by 2K of unused space. To change the font, the CPU simply writes the changes into the packed area and they are unpacked and used automatically by the copper and blitter. If HiRes characters are in use or the character height is not eight, the font is only stored in unpacked form with 16 by 16 bits per character. Again valid pixel data is only in the eight most significant bits of each word, but each character supports up to 16 words. If the font needs to be changed on the fly the CPU must store new data bytes at unpacked word intervals, as unpacking is only automatic for LowRes 8 x 8 characters. For ease of addressing the number of rows in each character should be a power of two even if the number of rows used is not - e.g. 16 for 10, 12 or 16 pixel tall characters, and eight for characters up to 8 pixels tall. The order of characters corresponds to the character code from 0 to 255, and need not be standard ASCII - it could be a variant like PET or TRS-80 ASCII or something entirely different, such as ZX-80 or ZX-81 character codes. You can switch between two fonts at the same Chip address MOD 65536 with just two POKEs to the copper list, setting BLTAPT.H and BPLBPT.H to point to the required page. Bitplane sizes: 12K for 64x16 characters of 8x12 pixels each (HiRes 512 x 192) 8000 bytes for 40 x 25 8 x 8 pixel characters (LoRes 320 x 200) A conventional Amiga bit map with one word for 16 pixels and lines consecutive in memory. Allow for HeadLines and border pixels, if any, adding to the size of the bitplane. There is a gap of 128 bytes at the end of the bitplane before the font data starts. Character Map size: 1K to 2K typically. One byte for each character to be displayed, as a linear array with no gaps between lines, e.g. the data for the second line of a 32 column x 16 line screen starts with the 33rd byte. These bytes are shifted by four or five places and inserted into the Copper List. In modes with no more than 1024 characters this is automatically unpacked into the Copper list before each new field is displayed. Copper List: A few hundred bytes of overhead + 20 bytes per character to blit This is the clever bit. It contains anything up to 45K of Copper instructions which must be executed every field (i.e. 50 times per second). These start by setting up the display, but most of their effort goes into programming the blitter to read the font for each byte in the character map and copy it to the correct place in the Bit Map. Optional coloured stripes can show the time when the copper is busiest and confirm beam avoidance without double buffering. If automatic font expansion is in use then the blitter unpacks the 8 x 8 pixel font patterns into 8 x 16 bit words before it starts to blit characters. Unpacking is performed in two passes, ascending and descending. The blitter avoids hogging the bus (not 'nasty') so the CPU can finish off font writes without falling behind the beam, at least during the ascending pass. If it's still writing during the descending pass, those updates will not appear in the unpacked font till the next field. The blitter is switched into 'nasty' mode for the individual HiRes character blits, which are short and well spaced so the CPU can get through between them - although it will have to wait much more than if it got its writes done earlier in the field. In general, the earlier the better.