Customizing the ANSI / VT-100 Terminal board firmware

Written May 12th, 2018 by

The ANSI / VT-100 Terminal board for the RC2014 computer uses the open source firmware available on GitHub and therefore it can be freely modified by anyone. However, being written with the Parallax Propeller microcontroller’s own Spin language, it may be difficult, even for those who are familiar with microcontrollers programming, to understand how it works, therefore I will try to explain as simply as possible how to do some minor changes to make the firmware a bit more personal.

Propeller ANSI / VT-100 Terminal

Propeller ANSI / VT-100 Terminal

The indications given assumes a certain familiarity with the use of the command line tools through the Linux Terminal or the Windows Command Prompt and that you are in possession of either a stand alone programming device capable of writing the 24LC256 serial EEPROMs, or a PropPlug serial adapter for in-ciruit programming.

Firmware compile

First you need to download the source code from GitHub to your computer to be able to edit it.

If you have the git program already installed, the simplest method is to “clone” the repository, that is to create a copy of the online repository on your computer. To do so, open a Terminal window (or Command Prompt if you are using Windows) and type the command

git clone https://github.com/maccasoft/propeller-vt100-terminal.git.

After a few moments in which the program downloads the contents of the repository, you will find the complete source in the propeller-vt100-terminal folder.

Example:

marco@bridge:~$ git clone https://github.com/maccasoft/propeller-vt100-terminal.git
Cloning into 'propeller-vt100-terminal'...
remote: Counting objects: 130, done.
remote: Compressing objects: 100% (13/13), done.
remote: Total 130 (delta 7), reused 7 (delta 2), pack-reused 115
Ricezione degli oggetti: 100% (130/130), 641.75 KiB | 378.00 KiB/s, done.
Risoluzione dei delta: 100% (72/72), done.
Checking connectivity... fatto.

If you do not have git and / or do not want to use it, you can still download a copy of the repository from GitHub using this link https://github.com/maccasoft/propeller-vt100-terminal/archive/master.zip. Unpack the archive and you will find a folder with the name propeller-vt100-terminal-master containing the complete source.

Now it is necessary to download and install the OpenSpin compiler necessary to compile the source code in the binary format readable by the microcontroller. The program can be downloaded from the download page. Note: the version on that page has been compiled directly from the sources and contains some corrections necessary to compile our code, if you have an older version of OpenSpin I recommend updating it.

The program does not need any installation, unpack the archive and you will find the executable program called openspin(.exe). I suggest to move the executable in a folder reachable by the PATH environment variable so you don’t have to type the full path every time.

At this point you are ready to compile the source, change to the folder where it was downloaded and type the command

openspin -b -u -DKEYMAP_IT vt100.spin

Without going too far explaining the meaning of the parameters, -b and -u are needed to generate the binary file from the source code, the -DKEYMAP_IT parameter defines the keyboard mapping to use, in this case Italian, and vt100.spin is the main source file to compile. All other files are loaded automatically and doesn’t need to be specified.

Compilation example:

propeller-vt100-terminal$ ./openspin -b -u -DKEYMAP_IT vt100.spin 
Propeller Spin/PASM Compiler 'OpenSpin' (c)2012-2018 Parallax Inc. DBA Parallax Semiconductor.
Version 1.00.81 Compiled on Apr 27 2018 08:13:09
Compiling...
vt100.spin
|-usb-fs-host.spin
|-com.serial.spin
|-com.serial.terminal.spin
  |-com.serial.spin
  |-string.integer.spin
|-waitvid.80x25.driver.spin
|-generic8x16-2font.spin
|-keymap_it.spin
Done.
Unused Method Elimination:
   55 methods removed
    1 objects removed
 1092 bytes saved
--------------------------
Program size is 14460 bytes

If everything went well and no errors are displayed, you can find the vt100.binary file resulting from the compilation. That file can be written to the EEPROM with the Propeller Loader utility, if you have a PropPlug serial adapter, or with a stand alone serial EEPROM programmer.

If your keyboard is not Italian or you want to use a different mapping, replace the last two letters of the parameter -DKEYMAP_IT with the country code you want to use among those available. For example openspin -b -u -DKEYMAP_US vt100.spin to set the mapping for US keyboards, or openspin -b -u -DKEYMAP_UK vt100.spin for UK keyboards. The currently available countries are DE (Germany), FR (France), IT (Italy), UK (United Kingdom) and US (United States).

Once verified that the compilation works correctly, you can proceed with the customizations. Remember that every time you modify the source code, it is necessary to repeat the compilation and writing on the EEPROM procedure as described above, otherwise your modifications will have no effect.

Cursor

With the default settings a large blinking block is displayed as cursor, like the ’70-style terminals. The variable cursor.byte at line 103 of the vt100.spin source allows you to change this setting.

cursor.byte{CM} := (cursor.byte{CM} & constant(!CURSOR_MASK)) | constant(CURSOR_ON | CURSOR_BLOCK | CURSOR_FLASH)

The CURSOR_ON parameter indicates that the cursor is visible, CURSOR_BLOCK indicates that it should be displayed as a large block and CURSOR_FLASH indicates that it should blink. By changing these parameters, you can change the default cursor appearance, for example:

cursor.byte{CM} := (cursor.byte{CM} & constant(!CURSOR_MASK)) | constant(CURSOR_ON | CURSOR_ULINE | CURSOR_FLASH)

Displays the cursor as a small rectangle at the character’s base, like the old MS-DOS style.

Replacing CURSOR_FLASH with CURSOR_SOLID will result in a steady cursor, not blinking.

cursor.byte{CM} := (cursor.byte{CM} & constant(!CURSOR_MASK)) | constant(CURSOR_ON | CURSOR_BLOCK | CURSOR_SOLID)

The possible parameters are listed at the beginning of the source.

    CURSOR_ON    = vga#CURSOR_ON
    CURSOR_OFF   = vga#CURSOR_OFF
    CURSOR_ULINE = vga#CURSOR_ULINE
    CURSOR_BLOCK = vga#CURSOR_BLOCK
    CURSOR_FLASH = vga#CURSOR_FLASH
    CURSOR_SOLID = vga#CURSOR_SOLID

Note: some VT-100 sequences allows to change the cursor style, the default setting remains in use until such sequences are used.

Special Keys

A normal keyboard does not only have alphabetic and numeric keys but also numerous special keys, such as function keys, directional arrows, etc. that in the PC environment allows to perform special operations. In a VT-100 terminal some of these keys have a sequence of codes assigned, others have no functions, simply because they were not originally provided with the keyboards of the time. The firmware allows to assign a sequence of codes to each key, if the default setting does not satisfy you, let’s see how to change it.

At the bottom of the vt100.spin source there is a long list of labels with a sequence of bytes assigned to them.

...
strKeyInsert        byte    0
strKeyHome          byte    $1B, "[H", 0
strKeyPageUp        byte    0
strKeyDelete        byte    $7F, 0
strKeyEnd           byte    $1B, "[K", 0
strKeyPageDown      byte    0
strKeyUp            byte    $1B, "OA", 0
strKeyDown          byte    $1B, "OB", 0
strKeyLeft          byte    $1B, "OD", 0
strKeyRight         byte    $1B, "OC", 0
strKeyShiftLeft     byte    0
strKeyShiftRight byte 0
...

The labels should be self explanatory about which key they refers to. In this example you can see that the Insert key doesn’t have any code (0) assigned to it, while the Home key has the sequence $1B, “[H” assigned. Therefore every time the Insert key is pressed, nothing is transmitted to the computer, while when the Home key is pressed the sequence written next to the label is transmitted to the computer and, if the program in use is able to recognize and manage it, will obtain an effect. The same obviously applies to all other labels and the corresponding keys.

If you want to change the codes that are sent by pressing a particular key, find the corresponding label and change the sequence after the byte instruction according to your needs. Remember to end the sequences with the code 0 which, as in the C language, indicates the end of the “string” of codes.

For example, suppose you want to send the word “INS” when the Insert key is pressed. Find the strKeyInsert label and change the sequence after the byte instruction as follows.

...
strKeyInsert        byte    "INS", 0
...

Now, after compiling the source and writing the binary code on the EEPROM, every time you press the Insert key you’ll see the word INS appearing on the screen.

You can also write the sequences using the decimal or hexadecimal value for each byte to send, separating them with a comma.

...
strKeyInsert        byte    "INS", 13, 0          ' adds the carriage return code 13
...
strKeyInsert        byte    $49, $4E, $53, $0D, 0 ' same as above but hexadecimal
...

Remember that the special code sequences must be interpreted by the program that is “running” on the computer, if they are not recognized they will not get the desired result and in some cases could cause unwanted effects.

Colors Palette

The VGA driver allows to display characters with up to 16 colors, from a palette of 64, using a mapping similar to that of the early VGA cards, with up to 16 colors for the foreground and 8 colors for the background. The palette is defined in the waitvid.80×25.driver.spin file starting from line 70 to line 85.

                long               $82020282, $22020222, $A20202A2, $0A02020A, $8A02028A, $2A02022A, $AA0202AA
                long    $02828202, $82828282, $22828222, $A28282A2, $0A82820A, $8A82828A, $2A82822A, $AA8282AA
                long    $02222202, $82222282, $22222222, $A22222A2, $0A22220A, $8A22228A, $2A22222A, $AA2222AA
                long    $02A2A202, $82A2A282, $22A2A222, $A2A2A2A2, $0AA2A20A, $8AA2A28A, $2AA2A22A, $AAA2A2AA
                long    $020A0A02, $820A0A82, $220A0A22, $A20A0AA2, $0A0A0A0A, $8A0A0A8A, $2A0A0A2A, $AA0A0AAA
                long    $028A8A02, $828A8A82, $228A8A22, $A28A8AA2, $0A8A8A0A, $8A8A8A8A, $2A8A8A2A, $AA8A8AAA
                long    $022A2A02, $822A2A82, $222A2A22, $A22A2AA2, $0A2A2A0A, $8A2A2A8A, $2A2A2A2A, $AA2A2AAA
                long    $02AAAA02, $82AAAA82, $22AAAA22, $A2AAAAA2, $0AAAAA0A, $8AAAAA8A, $2AAAAA2A, $AAAAAAAA
                long    $02565602, $82565682, $22565622, $A25656A2, $0A56560A, $8A56568A, $2A56562A, $AA5656AA
                long    $02C2C202, $82C2C282, $22C2C222, $A2C2C2A2, $0AC2C20A, $8AC2C28A, $2AC2C22A, $AAC2C2AA
                long    $02323202, $82323282, $22323222, $A23232A2, $0A32320A, $8A32328A, $2A32322A, $AA3232AA
                long    $02F6F602, $82F6F682, $22F6F622, $A2F6F6A2, $0AF6F60A, $8AF6F68A, $2AF6F62A, $AAF6F6AA
                long    $020E0E02, $820E0E82, $220E0E22, $A20E0EA2, $0A0E0E0A, $8A0E0E8A, $2A0E0E2A, $AA0E0EAA
                long    $02CECE02, $82CECE82, $22CECE22, $A2CECEA2, $0ACECE0A, $8ACECE8A, $2ACECE2A, $AACECEAA
                long    $023E3E02, $823E3E82, $223E3E22, $A23E3EA2, $0A3E3E0A, $8A3E3E8A, $2A3E3E2A, $AA3E3EAA
                long    $02FEFE02, $82FEFE82, $22FEFE22, $A2FEFEA2, $0AFEFE0A, $8AFEFE8A, $2AFEFE2A, $AAFEFEAA

That seems like a pile of meaningless data, but in reality it is very easy to decipher. Each row represents a foreground color and each column represents a background color, so we have a grid of 16 rows and 8 columns. Each cell of the grid is group of 8 hexadecimal digits representing the RGB color to use for the background and foreground in normal mode or when the character is blinking.

For example, let’s see how the the white foreground on a blue background is defined in the palette. The white foreground color is on line number 16, the blue background color is on column 5, so as you would do with the “battleship game”, scroll the list from top to bottom up to line 16, then from left to right up to column 5, there is the hexadecimal code $0AFEFE0A defining the color combination. Each 2 hexadecimal digits represents a byte and an RGB color according to the following scheme.

| 7 6   5 4   3 2   1 0 | 7 6   5 4   3 2   1 0 | 7 6   5 4   3 2   1 0 | 7 6   5 4   3 2   1 0 |
+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| r r | g g | b b | 1 0 | r r | g g | b b | 1 0 | r r | g g | b b | 1 0 | r r | g g | b b | 1 0 |
+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| 0 0 | 0 0 | 1 0 | 1 0 | 1 1 | 1 1 | 1 1 | 1 0 | 1 1 | 1 1 | 1 1 | 1 0 | 0 0 | 0 0 | 1 0 | 1 0 |
+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|       0A (blue)       |      FE (white)       |      FE (white)       |       0A (blue)       |

From left ro right, the first byte is the color to use as foreground when blinking, the second byte is the color to use as background color when blinking, the third byte is the foreground color to use when not blinking and the fourth is the background color to use when not blinking.

Each byte represents the RGB color. Bits 7-6 are the red component intensity, bits 5-4 the green component intensity and bits 3-2 the blue component intensity. Bits 1-0 must be kept at the default values 1 and 0 respectively. Since each component is represented by 2 bits there is a maximum of 4 intensity levels for each color (00, 01, 10 and 11) then 4 * 4 * 4 = 64 possible combinations that correspond to as many color tones on the screen. For example $02 is black, $C2 is red, $32 is green, $0E is blue, $FE is white, and so on.

To change the foreground color to a yellow for example, you need to change the value of the two central bytes (FE) so the red and green components are at maximum intensity, and the blue component is at zero.

| 7 6   5 4   3 2   1 0 | 7 6   5 4   3 2   1 0 | 7 6   5 4   3 2   1 0 | 7 6   5 4   3 2   1 0 |
+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| r r | g g | b b | 1 0 | r r | g g | b b | 1 0 | r r | g g | b b | 1 0 | r r | g g | b b | 1 0 |
+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| 0 0 | 0 0 | 1 0 | 1 0 | 1 1 | 1 1 | 0 0 | 1 0 | 1 1 | 1 1 | 0 0 | 1 0 | 0 0 | 0 0 | 1 0 | 1 0 |
+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|       0A (blue)       |      F2 (yellow)      |      F2 (yellow)      |       0A (blue)       |

If you replace the original $0AFEFE0A value in the table above at line 16 and column 5 with the value $0AF2F20A you’ll get the yellow color on a blue background instead of white. Try to run the following from BASIC, before and after the change.

PRINT CHR$(27);"[1;37;44m";"WHITE ON BLUE;CHR$(27);"[0m"

Now you can have fun creating the color palette that suits you needs.