Text on TV

Control Sequence Handling

This section is an explanation of the state machine which determines what action should be taken for the characters that are received through the serial line.

For details of the actual control code sequences used, see the User Guide.

The device should handle character sequences in largely the same manner as a VT52 terminal.

It will not be identical for the following reasons

The following diagram shows a rough outline of the state machine:

Control Code State Machine

Control Code State Machine

Note: This is not a full-blown state diagram - It's what was used to write the code without getting in a pickle.

NULL handling is not shown; The <NUL> character is ignored in all states. They can be sprinked throughout the input stream to no effect.

The DLE state is not as complicated as it looks - it simply allows any character to be output to the screen, temporarily ignoring any alternative meaning for that character's code.

e.g. <DLE><ESC> outputs character 27 (a left-arrow) to the screen, rather than putting the machine into the Escape state.

The 3 DLE sub-states are simply to show a return to the original state afterwards.


There is a character polling routine for each of the main states in the machine :

A global variable, g_char_poll, points to the current polling routine. This pointer effectively holds the current state of the machine.

The polling routines are very simple. All they do is look up which processing routine should be called (later on) as a response to the character received.


A global variable, g_char_process, points to the processing function selected by the poll routine.


Each polling routine has roughly the following structure:

    if a new character has been received
        //store character
        //set the current character processing routine (g_char_process), depending on which character was received
        /* This is often achieved using lookup tables */
        //set the current character processing routine (g_char_process) to the 'null' handler

Because the poll routines are called in timing-critical sections of the sync routines, they have a very strict requirement: They must execute in a specific number of cycles - no matter which execution-path through the routine is taken. Fortunately they are few in number, simple and very similar to each other, so balancing the code with careful numbers of "nop" instructions (or their equivalent) is not too tricky. The precise number of cycles that the routine must execute in is defined in code.

poll_split is a special state. It is used in cases where the character processing routine simply cannot complete in a single scanline (for example 'clear from start-of-screen to cursor'). For these routines, the work is split into two. The first half is done in the first scanline by the first character processing routine. The 'split' state is then chosen (not shown in the diagram above) which simply causes a second character processing routine to be executed in the second scanline. The split-state automatically reverts to the ground state after the second scanline's processing is complete.

Character Processing:

These routines actually do the real work. They perform the actions requested via the serial stream.

Some examples are:

Each of the routines have a rather limited time in which to run - a little less than 8.5us. That's less than 136 clock cycles (at 16Mhz), so the more complex functions need to be efficient.

Unlike the Polling routines, these diverse processing routines don't have to execute in a fixed duration, they merely have to complete inside a maximum duration. The precise start time required for the pixel signals is achieved by the Display Start interrupt. That interupt fires after the character processing routine has run.

[ This 'Display Start' point is no longer found using sleep/wake-on-interrupt. See the Basic Operation section for details. ]

The null_process routine is called whenever no character or a <NUL> character is received. It 'normalises' the current row if neccesary. Normalising is carried out on a row that has its attribute byte marked as 'show as blanks'. Normalising simply clears all the characters in the row to blank characters and clears the attribute byte (so that the now-full-of-blanks row is 'displayed' normally).