CHARMODE
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)
fount support for up to 256 characters each up to 16 pixels tall
support for dynamic fount changes from one field to the next


Remaps screens encoded as one byte per character into Amiga bitplane graphics.


Uses the copper to generate a custom screen mode and convert character codes into graphic patterns from a fount, by programming the blitter. Processing is entirely 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.

CharMode is intended for use in emulators for vintage computers 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.


Uses a fount of 256 characters, each eight pixels wide and up to 16 pixels tall, with one byte, left justified, in each pattern WORD. Thus the fount 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 fount changes are supported on a per-field basis by writing the changed data to the corresponding fount words. These need to be unpacked as the fount must use one WORD per character line (as it's a 16 bit blitter). Automatic unpacking of 8 x 8 founts 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. The following lists 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 fount 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 fount updates, as it's rare that the entire fount 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 fount 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 fount 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 the current version 0.13, the code has not been 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 fount into internal format for blitting
(3) Convert an eight 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 (AutoChar_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.


Other subroutines in the Compiler include a simple Disassembler for checking copper lists, fount format converters, and test routines to update the display with the CPU (rather than the copper) controlling the blitter.


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: ascending pass of blitter unpack. CPU update can overlap if working forwards
Red: descending pass of blitter unpack - CPU changes in this phase will be displayed
one field late - but blitter is not BUSY so CPU writes go at reasonable speed.
Blue: rendering characters by blitting patterns from the fount. This area should end
before the start of display of the last character line.


BlitFount 0 or 1 Flag - expand 8 x 8 fount 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 fount.


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 fount into the copper list at a corresponding position. For instance
with an 8 pixel tall fount, 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 fount 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. Founts 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 16 bit alignment reasons.


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 Fount, 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 fount 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 fount unpacking for 8 x 8 character founts. This suits all the machines (Atari, C64, MSX, Sinclair ZX-81) which require it. Taller founts 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@silicon.studio.co.uk, 3 November 1996

DATA STRUCTURES

The CharMode data structures start on a 64K boundary in Chip memory. Fount 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.


Fount size: 8K (allocated - may not be all used)


If LoRes 8 x 8 characters are in use the fount 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 fount, 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 fount 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 fount 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 founts 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 64 x 16 characters of 8 x 12 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 fount 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 size: 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 fount 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 fount expansion is in use then the blitter unpacks the 8 x 8 pixel fount 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 fount 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 fount 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.