CMM2 graphics examples and explanation


Author Message
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 11174
Posted: 01:58pm 11 May 2020      

Graphics modes

This post will introduce the 5 graphics resolutions on the CMM2 and the three colour depths in more detail and the use of the MODE command. I will mention the 12-bit modes but will reserve a full explanation of these until a separate tutorial. Also, still to come in this series is more on the PAGE command, using the basic graphics in a more efficient way and, of course, sprites.

A lot of this post will be slightly theoretical, but the more you understand what is going on under the cover of the CMM2 the better you will be able to make the most of its capabilities.

Lets start by being a VGA monitor and asking it to show the Display Information for the signal it is receiving. This isn't affected by the colour depth.

Mode 1 (800x600) : Monitor sees 800x600 @ 60Hz
Mode 2 (640x400) : Monitor sees 640x480 @ 75Hz
Mode 3 (320x200) : Monitor sees 640x480 @ 75Hz
Mode 4 (480x432) : Monitor sees 640x480 @ 75Hz
Mode 5 (240x216) : Monitor sees 640x480 @ 75Hz

That looks a bit odd - lets explain.
VGA monitors, particularly modern TFT types, have a limited range of signal timings that they will accept and sync up properly. We have chosen to use just two that are supported by all monitors. 800x600 @ 60Hz needs a 40MHz pixel clock. 640x480@75Hz needs a 31.5MHz pixel clock. All other timings, vertical sync and horizontal sync are defined as a multiple of the underlying clock period.

Before going into this in more detail I'll answer the question - why 640x400 and not 640x480?

In the first post I explained the way memory is used for the graphics pages and that 800x600 resolution with 1 byte per pixel uses 480,000 out of 512K (512*1024=524288) bytes of the fastest memory. 640x480 would only use 307,200 bytes so no problem BUT 640x480 with 16-bit colour would use 614,400 bytes - more than we have available.
640x400x2 = 512,000 just inside the 512K limit allowing full colour images to be displayed from the fastest memory.

Moreover, the main retro computers Commodore64, Amiga, Atari-ST all used 320x200 as their main graphics resolution, the same as our mode 3 and 640x400 is exactly double this.

But, I hear you say, The manual says the CMM2 supports 800x600x16-bit resolution amongst others. Yes, but in this case the first megabyte of the 3 MBytes of SDRAM is used as video display memory - page 0. The SDRAM isn't as fast as the internal memory but in most cases works perfectly well in this mode and it is great for displaying pictures. With a pixel clock at 40MHz and 2 bytes per pixel the LTDC is reading that memory at 80Mbytes/second. Even more extreme is 12-bit mode. Without going into the explanation  here 12-bit mode uses 4 bytes per pixel so 800x600x32-bit now needs memory to be read at 160Mbytes/second and uses 2Mbyte of SDRAM!

The SDRAM is also used for MMBasic arrays and things like buffers for playing MP3 and FLAC files. If you set the video mode to 800x600 12-bit resolution and play a MP3 file and run a program then occasionally the monitor may flicker or lose sync. The extent to which this happens is very much monitor dependent and while there may be no issue on your monitor there may be for other people.

For this reason the graphics modes that use the SDRAM as the main display memory are only enabled if you execute

OPTION MODES UNLOCKED

and you are discouraged from distributing programs that use these modes.

Back to the main story.
Supported resolutions
Mode 1 displays 800x600 pixels and the LTDC just reads out the bytes one at a time at 40Mhz. Bytes are not read during line and frame blanking periods (HSYNC and VSYNC) and the LTDC handles this for us.

Mode 2 displays 640x400 pixels but the monitor sees 640x480. This is achieved by increasing frame blanking by 80 lines split between the top and bottom (front porch and back porch in the jargon). The memory used is 640x400 pixels and this is arranged exactly like we saw for mode 1 earlier, top left to bottom right.

Mode 4 displays 480x432 pixels for Maximite compatibility. This is achieved by reducing the pixel clock to 31.5/640*480 = 23.625MHz but maintaining the same absolute durations for line and frame blanking periods. In this case frame blanking is extended by 48 lines split between the top and bottom. The change in pixel clock rate isn't a perfect solution, particularly with LCD monitors. These work by splitting the period between HSYNC pulses and sampling the analog signal at the expected pixel rate. So they are sampling at 31.5MHz and we are changing values at 23.625Mhz. They then need to map this to the physical screen resolution (say 1280 pixels) so, depending on the monitor, you may see slight artifacts.



Notice how the left side of the "0" in "11-05" is wider than the right. This is a monitor sampling issue and probably wouldn't occur on an old analogue CRT monitor. The memory used is 480x432 pixels and this is arranged exactly like we saw for mode 1 earlier, top left to bottom right.

Mode 3 displays 320x200 pixels. You can probably easily guess now how the 320 is created. The pixel clock is halved from 31.5MHz to 15.75MHz. As this is a simple power of 2 there is no sampling issue like we saw with mode 4. However the monitor is still expecting 480 lines and that can't be changed. Unfortunately the LTDC doesn't have the capability to clock out each line of data from memory twice so we have had to replicate each line in the firmware. The memory used is therefore only half that of 640x400 rather than a quarter. As for mode 2 frame blanking is increased by 80 lines split between the top and bottom. The memory used is 320x400 pixels and to get the address of a pixel in memory we now have to compensate for each line being duplicated. e.g.

for an 8-bit colour depth:
add1% =MM.INFO(page address 0)+ (y * 2) * MM.HRES + x
add2% =MM.INFO(page address 0)+ (y * 2 + 1) * MM.HRES + x

Mode 5 displays 240x216 pixels and I'll leave to the reader to extrapolate from modes 3 and 4 as to how this works.

Supported colour depths

The CMM2 supports 3 colour depths

8-bit uses 1 byte per pixel and uses RGB332. That means there are three bits coding the red intensity (8 levels), three for green (8 levels) and two for blue (4 levels). However, as we saw earlier we can change this using the MAP command and function so, for example, we could create a grey-scale map with 256 different "grey" levels.

16-bit uses 2 bytes per pixel and uses RGB565. That means there are five bits coding the red intensity (32 levels), six for green (64 levels) and five for blue (32 levels). Although this may seem limited, colour images displayed in RGB565 on a normal monitor are pretty much indistinguishable from those with greater colour depth.

12-bit uses 4 bytes per pixel and uses ARGB4444. That means there are four bits coding the red intensity (16 levels), four for green (16 levels) and four for blue (16 levels). In addition there are 4-bits that encode the transparency. i.e. the extent to which the colour masks anything underneath it. Think about paint. If you paint white on top of red you will still see a pink blush after the first coat. Although the white paint is "pure" white it allows some of the red to be seen through it. In the case of ARGB4444 the 4 "A" bits range from 15=solid to 0=totally transparent.
Now, transparency isn't much use if there is nothing to be transparent over. So 12-bit mode has two layers and hence it takes 4 bytes per pixel - more on this later.

In all cases the firmware translates from the colour specification you provide which is in RGB888 or for 12-bit mode ARGB8888 to the internal format for the display colour depth selected. This means the Basic code is identical irrespective of colour depth (except if you use the MAP command in 8-bit mode or are using Maximite compatibility mode)

The MODE command

The explanation of the mode command in the user manual is very comprehensive so there is little point in reproducing it again here. However, of particular importance is the number of video pages that are available in each mode. The firmware rounds up the amount of video memory used for any resolution/colour depth to the nearest multiple of 128Kbytes (128*1024=131072). So 320x200x8-bit seems like it should need 64000 bytes but remember each line must be duplicated so it really needs 128000 which is less than 131072. This means we can have 4 video pages in the fast internal memory (512K available) and another 24 in the 3Mbytes of SDRAM. As before MM.INFO(PAGE ADDRESS n) can give us the address of any of the pages. Page 0 is always the display page but when programming for performance there is an advantage to using a page in the faster memory for other uses.

The syntax of the mode command is:

MODE mode, bits [, bg [, int]]

We now know all about the mode and the bits and what the firmware is doing behind the scenes to create the images. We will deal with bg which is only used in 12-bit mode later but for now we can look at the "int" parameter.

int is the name of an MMBasic interrupt routine which is called immediately after the last active row of data is clocked out. This is before the actual frame-blanking pulse (actually it occurs at the beginning of the front-porch period which bizarrely is at the end of the frame whereas the back porch is at the beginning - WTF?). This is important to understand because before any new data is read from the memory the front porch period, the actual VSync frame pulse period and the back porch period will all have elapsed. In CMM2 terms this can be quite a long time.

For Modes 2 and 3, there are 400 lines displayed and the complete frame has 500 line periods so there is a dead time of 2.66 milliseconds when the display can be written and no screen artifacts will ever appear.
For Mode 1 this period is 0.74mSec and for modes 4 and 5 it is 1.65mSec

You can actually effectively extend this period if you arrange your code to write from the top of the screen downwards. As long as your writes keep ahead of the LTDC reading the memory then there will be no artifacts or tearing.

Lets build a very simple digital clock with big digits using the frame interrupt and show that there are never any screen artifacts


' set to 320x200 mode, with a frame interrupt
' NB the background colour must be specified but is ignored in this mode
mode 3, 8, 0, frameint

' set a global variable that will be updated by the frame interrupt
fi%=0

' loop forever
do
  do
  loop while fi%=0 'wait until the frame interrupt has happened

' write out the time in the middle of the screen
  text mm.hres\2, mm.vres\2, time$, CM, 6,, RGB(red)

' prime ready for the next interrupt
 fi%=0

loop


sub frameint
 fi%=1
end sub







Note that I am not doing any screen updates in the interrupt routine - you wouldn't do that would you? Interrupts must always be kept short and the best approach is always to set a flag that can be processed in the main program.
Edited 2020-05-12 00:11 by matherp