27 August 2004 Text in color Altering the design If you have several methods of doing something, how do you choose the best method? Text output I asked myself that question as I was reconsidering my scheme for printing text in Karig. I originally planned for a T register, a text buffer provided by the system, which you'd use for printing text to the screen: You'd build up the text by appending characters to the buffer, then you'd print the complete buffer contents to a given row on the screen. I was thinking about how to make the whole system as simple as possible, and providing a single method that required you to specify only a row number seemed like a way to do that. However, the design of the status bar, with a number of discrete "fields" of data, made it clear that I'd need to print a handful of characters at a time to a particular row and column. So I added two variables to karig.asm — _row and _col — to hold the location of the next character to be printed. I wrote emit, which takes a character on the stack. If the current character location is invalid, the routine returned with the carry flag set to indicate an error, and nothing was printed; otherwise the character would be printed and the current column would be incremented. (Emit never altered the row, so emit always added a character to the current row, as long as the column position was not beyond the right edge of the screen.) I introduced this version of emit in my previous entry. To supplement emit, I added emitline, which took the address of a string and passed each character in the string to emit and then moved to the next line (it incremented _row and set _col to zero). This was good enough for printing short messages to the screen to prove that a stretch of code worked. To print a string without moving to the next line, I wrote emitstring. So at this point I had three routines for printing to the screen. I thought that I should have only one. To fix this, I removed emitline, I renamed emit to _emit, and I renamed emitstring to emit. Thus I had two routines: _emit for printing characters, and emit for printing strings. In my scheme, routines whose names begin with an underscore are "private" in the sense that I won't be publishing them in the wordlists; only other routines within the Karig kernel will be able to call them. Thus the routine to print a single character is private; the programmer should use only emit, which is more useful anyway because you'd use it to print a complete phrase in one go. I have other routines now: newline moves to the next line (row) on the screen, and clearline overwrites the entire current line with spaces (painting the line in the current background color), then calls linehome, which sets _col to zero. (I may yet remove clearline and replace it with something else, such as a routine to change the background color of an arbitrary rectangle of character positions on the screen, or simply a routine to change the background color of all twenty-eight lines of the block-viewer area.) (I am also thinking about adding — I have not yet added — a word ?wrap to take the address of a string, and to return yes [i.e., to return the zero flag clear] if the string is too long to fit on the current line and therefore should be "wrapped" to the next line. As long as I send only single words to ?wrap, I can implement wordwrap with a phrase like nextword ?wrap if newline then emit [i.e., if the word doesn't fit, wrap it]. I find that if I can express an idea in a phrase this simple, then I get a sense of satisfaction, a sense that the words are almost certainly right. So I'm happy with this new emit.) Text color I designed my text-color routines three times. My first design was a routine named color, which was going to take two arguments: the foreground (character) color, and the background (highlighting) color. On the other hand, if colors were always going to be 16-bit values, why not pass one argument containing both 16-bit values? I wasn't satisfied with this, so I asked myself: What would work best for me as a Korth programmer? What would I need, what will work best, when I'm writing code for the editor in Korth? First, it is possible, though not likely, that Karig will be changed at some point to allow for a 24- or 32-bit color graphic mode, so it didn't make sense to tie Karig's API to 16-bit colors, so the API should not require 16-bit color values as arguments to any of its routines. Second, the 24-bit RGB (red-green-blue) color value is a familiar and useful standard. Style sheets for web pages, for example, use 24-bit color values, such as #FF0000 for red, #00FF00 for green, and #0000FF for blue. It's even easy to write, because each of the three color components gets its own byte, and therefore its own pair of hex digits. If you know that you get a particular shade of blue by specifying 80 (0x50) for red, 184 (0xB8) for green, and 228 (0xE4) for blue, then you know that the 24-bit value for that particular shade is 0x50B8E4. Finally, I figured that I'd need to change the foreground color more often than the background color. I expected that on most occasions I could set the background color once but might change the foreground color relatively frequently, as when printing ColorForth pre-parsed words to the screen. So it made sense to be able to change one color without changing the other. So I retired color and wrote two new text-color routines. Each routine takes a single 24-bit RGB value on the stack. Each routine is simple to use and was simple to implement, so I'm happy with the new routines. Code My new code makes the changes I suggested above. It implements:
I also changed the macros. After I added the ColorForth compiler to karig.asm, I found that I had two groups of macro definitions. My own code defined macros _dup and _drop, while Chuck Moore's code used macros DUP_ and DROP to do the same thing. Because I'll probably borrow more of Chuck's code before I'm finished, I decided to drop my own macros and use Chuck's instead. I removed the macros that I defined but have yet to use (swap, nip, over, push, and pop) and replaced all instances of _dup and _drop with DUP_ and DROP. I also changed _lit to LIT to fit Chuck's style of capitalization. Finally, I commented out the startup code that compiles and runs the precode and added test code to print colored text to prove that the routines work. Results After Karig finishes booting, the screen is blank (all white) except for the top two lines, which look like this: (The top line is a simulation of the status bar I'm planning on creating for Karig, and the second line is of course a demonstration of how words in a ColorForth source listing would look on the screen.) This code worked perfectly the first time I ran it a couple of nights ago. No debugging this time! That's always nice of course, but I was especially happy about this because I made a relatively large number of changes to the code. (Maybe that's a sign I'm getting better at this?) I tweaked the colors a little after running the code, but that's it. Check the index for other entries. |