GLIR - Graphics LIbrary for RISC-V

Created for the University of Alberta CMPUT 229 Computer Architecture course

Author: Austin Crapo, June 2017

Converted for RISC-V: Taylor Zowtuk, May 2019


Information

GLIR is a collection of subroutines to emulate graphics and it is meant for use with the Xfce Terminal Emulator. GLIR can be run by using either RARS (master branch) or Freedom Studio (FreedomStudio branch). Although most UNIX-system terminals will work, the Xfce terminal was the only used for extensive testing during development (tests/examples also appear to work using GNOME Terminal). The true requirement is that the terminal emulator be DEC VT220 compliant. If the terminal emulator is not compliant GLIR subroutines will produce a bunch of garbled text. The purpose of GLIR is to abstract away the terminal escape sequences into an organized library that allows beginner assembly programmers to include visual elements in their work. GLIR has been modified from the original GLIM library to be used with the RISC-V ISA.

Access to the Code

The source code for GLIR can be found at: https://github.com/cmput229/GLIR.

Access to the API Reference

Some high-level library useage is presented here; for a list of all public subroutines and their useage/requirements, refer to GLIR's reference at: https://cmput229.github.io/GLIR/docs/reference.

License

GLIR is free to use, available under the MIT license. Attribution under this license is not required, but always appreciated.

Dependencies

GLIR requires a DEC VT220 compliant terminal and RARS (a RISC-V simulator) to run. On Ubuntu we can install an Xfce-terminal using the command sudo apt-get install xfce4-terminal. RARS requires at least Java 8 to run which we can install using the command sudo apt-get install default-jdk. Lastly, we can get RARS by going to the RARS github page and downloading the latest rars.jar release.

Usage

This session discusses how to start GLIR, the conceptual architecture, how the screen is updated, and support for colors.

Starting GLIR

Before you call any of the GLIR printing routines you must call the following routine:

jal    ra, GLIR_Start

This helper subroutine resizes the screen (see input arguments), hides the cursor (which otherwise will interfere with printing), and clears the terminal to the default background color.

When your program finishes, and if you had started GLIR, then call the following:

jal    ra, GLIR_End

GLIR_End undoes most changes so that the terminal window is not left unusable when your program exits. Unfortunately GLIR_End cannot resize the window back to its original size because at the moment there is no method to read and store the terminal size at the start of the program.

Conceptual Architecture

The main device in GLIR is the terminal window which contains a certain number of rows and columns. These are referred to throughout the documentation as Rows and Cols or (R, C).

If you stare at the space between the boxes long enough you'll probably see an optical illusion

The tuple describing a position on the grid is (R, C) and not (C, R). Remember this. Terminals were designed to print text top to bottom, left to right. Their underlying control structures are built on this assumption. Thus the row number comes before the column number. The origin (0, 0) is at the top left of the terminal window:

If you think this is confusing, imagine the confusion trying to program the library while transforming coords each time, the overhead would be awful and the bugs many.

The screen above is a square grid, but terminals and fonts are not square (even if they are monospace), thus a square grid renders a rectangular shape.

Screen Updates

The terminal display that you access with GLIR should be treated as a write-only persistent array. Thus, you can 'push' updates to the screen, but you cannot revert them, you cannot see if they were successful, and you cannot make decisions based on the screen state. Instead, the best method to use GLIR is to keep your own data structure representing the screen, then when a change needs to be made to that data structure, you can make a corresponding change in the screen by 'pushing' updates to it.

There are two main ways to handle the synchronization between your own data structure and GLIR. Depending on what you are trying to accomplish one will be preferable over the other, but the second is much better practice and more versatile.

Method 1 - Clear and Refresh

This is the brute force method. Each time any updates need to happen, you clear the entire screen (using GLIR_ClearScreen). Then you iterate over your data structure and use GLIR_PrintString to print each element of the display again.

Drawbacks: less built-in subroutine color support, if you need to reprint too often or have too many elements that need printing your screen will flicker uncomfortably. This is because clearing the terminal is very slow (it only requires one command), but printing requires that about 20 characters be printed for each tile you need to print on the screen. A monitor however refreshes very fast. So your monitor is likely to refresh at least once after you have cleared, but before you are done printing all the elements, causing the flicker.

Advantage: quick to setup for testing purposes so you can focus on getting the underlying code working.

Resources: see the GLIR_ClearScreen and GLIR_PrintString subroutines.

Method 2 - Batch and Release

This method focuses on calculating changes that need to be made to the screen, and then printing them. When your underlying data-structure representing the screen is changed, you add the changes to a list. When you have enough changes, or when you are finished calculations or ready to print, you then print all the updates at once, leaving the unchanged portions of the screen untouched. This can be important for things such as games or art, where you would rather have all the updates show at once or with as little delay between them as possible. There is a slight overhead in keeping track of this list, however, not nearly as much as clearing the whole screen and reprinting. Also, terminals themselves are slow to print so there's generally no way you can completely eliminate delay between the start and end of printing, but you can reduce it. GLIR has an included subroutine to assist with this concept for printing, that rolls together color printing in a simple 'batch job' format.

Advantages: less overhead, fewer updates, more built-in color support, potentially smoother animations

Resources: see the GLIR_BatchPrint subroutine.

Of course, there are other methods that can be designed using the individual subroutines and smart programming. Feel free to experiment. A hybrid approach might for example use GLIR_PrintString and GLIR_SetColor to achieve color printing and carefully be programmed to avoid reprinting too many tiles. It is left up to you how you use this library.

Color

Support for colors in terminals has been around for a long time - but universal adoption is not yet a reality. For those terminals that do support it, different levels and standards have been adopted as the terminal emulators matured. Seven color terminals were upgraded to 256 color terminals and then to a 3-byte Red-Green-Blue (RGB) color generation. GLIR is designed to support 256-color terminals or above (3-byte RGB terminals will also work because they also support the 256-color codes). The Xfce4-terminal is a 256-color terminal. Below is a table that shows the supported color codes (see GLIR_SetColor or GLIR_BatchPrint for usage).

In GLIR you change terminal color settings (using GLIR_SetColor) in the same way that you print to the terminal. You may 'push' a color setting, but you cannot check what the current setting is. Future prints will be in the color set, past prints will not be affected. Therefore, part of GLIR_Start is to set the color to the defaults (default is based on your terminal defaults, which are usually white text on black background in Linux). The GLIR_RestoreSettings subroutine can be used to restore the default color settings.

The following table is extracted from wikipedia:

256-color mode — foreground: ESC[38;5;#m   background: ESC[48;5;#m
Standard colors High-intensity colors
 0   1   2   3   4   5   6   7   8   9  10 11 12 13 14 15
216 colors
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
Grayscale colors
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255

Testing

Demonstrations of the expected output of the tests/demos, which are included with this library, can be found at: https://cmput229.github.io/GLIR/Testing/testing.

Additional Notes

If you create new subroutines or have any suggestions feel free to make a pull request and we will consider incorporating it. For any questions or problems with GLIR please create an issue on the repository. The purpose of GLIR is to abstract out the escape codes that move the cursor and change printing styles into a workable graphics package. Some terminal emulators allow more advanced capabilities that are not currently implemented in GLIR. Your terminal emulators' escape-code documentation contains information about such capabilities. A hint for those wanting to create animations or more pretty graphics - there is no character that can be printed that covers an entire grid space in most default fonts. The unicode 'full block' character makes for a great pixel element in most printing cases but leaves a sliver of space above and below but not side to side. If you find a square font where the full block covers, please let me know and I will look into including support for it in GLIR. A hacky method is to set the color of the background and print a space (" ") but this can effect GLIR_ClearScreen because it will set the entire screen to the current background color.

Contributions

Thanks to: AustinGrey, TaylorZowtuk, zacharyselk, rmnattas, and mehrabmehdi for their contributions to GLIR!