Demilade Sonuga's blog

All posts

Printing Hello World Again I

2022-11-19

To get printing text again, we have to use the raw pixels of our graphics mode. But before we proceed, we must first ask ourselves this question: what is text?

Consider the following string of characters: "A quick brown fox jumped over the lazy dog". As you read the text in quotes, a picture of a dog jumping over a fox may or may not have been conjured up in your mind, but whether or not it did, I believe I can safely assume that you understood the message/idea that those string of characters were arranged to represent.

The characters on their own actually have no meaning. Every single bit of meaning associated with a character or word (string of characters) or sentence (string of characters with some space characters included) is completely bestowed by us, the speakers of the English language.

As a matter of fact, the characters themselves are just dashes and squiggles of lines. The letter 'A' is just a dash in between two diagonal lines. The letter 'C' is just one really curved line. The letter 'F' is just one vertical line and two horizontal ones.

The letter 'A' (One horizontal line and two diagonals):
   /\
  /  \
 /----\
/      \
The letter 'F' (One vertical and two horizontal lines):
|--------
|
|------
|
|

This is the general idea we're going to use to draw our characters. But this isn't enough to get us printing text, yet.

The pages of the notebooks we write in are often ruled with equally spaced horizontal lines from the top to the bottom of the pages. It's within these spaces we draw straight and squiggly lines (characters) which, when strung together, have meaning either to us or to some other people who want to read it.

Writing in a notebook:

-------------------------------
The quick brown fox jumped
-------------------------------
over the lazy dog.
-------------------------------

When we write, the letters may or may not all have the same height and same width, depending on how we write. This doesn't matter because, with our eyes, we can still determine the next position to write the next letter in such a way that there will be no meaningless overlapping of characters and at the same time, the string of characters will still have their intended meaning. But computers, being the electronic machines they are, have no way of making such a determination without being explicitly programmed to do so.

To draw characters, first, we must have some description of how to draw each character, pixel by pixel, that is, we need a font. The screen width of our graphics mode (horizontal resolution (number of pixels in one row)) is 640 while the screen height (vertical resolution (number of pixels in one column)) is 480. We have to pick a width and a height of our characters such that a suitable number of characters fit in one line and the characters themselves aren't too big or too small.

In this project, our fonts will be 16x16 fonts, that is, they will be 16 pixels in width and 16 pixels in height. This gives us 640 / 16 == 40 characters on a line and 480 / 16 == 30 rows of lines. The screen will be just like a notebook, but without visible horizontal lines.

Our plan of attack goes like this:

  1. Design the font.
  2. Draw the characters from the font descriptions.

Don't worry about 2 for now. Let's just stick out with 1. The first step on this list is quite fun. What we do is, for each character we want to describe, we create a new array. This array will contain 16 other arrays. Each of those 16 other arrays will then contain 16 boolean values. Each of those 16 boolean arrays describes how to draw a row of the character on the screen. This is easily understood with an example:

0  - _______11_______
1  - ______1111______
2  - _____11__11_____
3  - ____11____11____
4  - ___11______11___
5  - __11________11__
6  - _11__________11_
7  - 11____________11
8  - 1111111111111111
9  - 11____________11
10 - 11____________11
11 - 11____________11
12 - 11____________11
13 - 11____________11
14 - 11____________11
15 - 11____________11

The above is the letter 'A' described in a 16x16 grid, where 1 means "color pixel" and '_' means "don't color pixel". The first row of the letter, row 0, is a "___11___", meaning "don't color", "don't color", "don't color", "don't color", "don't color", "don't color", "don't color", "color", "color", "don't color", "don't color", "don't color", "don't color", "don't color", "don't color", "don't color". In actual code, the '_' will be 'false', the '1' will be a 'true' and a procedure to draw the character will just be a loop over this description, coloring and not coloring when the description says so.

The above description of the letter 'A' in Rust will look like this:

// Description of the letter 'A'
const A: [[bool; 16]; 16] = [
   // The first row of the letter
   [false, false, false, false, false, false, false, true, true, false, false, false, false, false, false, false],
   // The second row of the letter
   [false, false, false, false, false, false, true, true, true, true, false, false, false, false, false, false],
   // The third row of the letter
   [false, false, false, false, false, true, true, false, false, true, true, false, false, false, false, false],
   // The fourth row of the letter
   [false, false, false, false, true, true, false, false, false, false, true, true, false, false, false, false],
   // The fifth row of the letter
   [false, false, false, true, true, false, false, false, false, false, false, true, true, false, false, false],
   // The sixth row of the letter
   [false, false, true, true, false, false, false, false, false, false, false, false, true, true, false, false],
   // The seventh row of the letter
   [false, true, true, false, false, false, false, false, false, false, false, false, false, true, true, false],
   // The eight row of the letter
   [true, true, false, false, false, false, false, false, false, false, false, false, false, false, true, true],
   // The ninth row of the letter
   [true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true]
   // The tenth row of the letter
   [true, true, false, false, false, false, false, false, false, false, false, false, false, false, true, true],
   // The eleventh row of the letter
   [true, true, false, false, false, false, false, false, false, false, false, false, false, false, true, true],
   // The twelfth row of the letter
   [true, true, false, false, false, false, false, false, false, false, false, false, false, false, true, true],
   // The thirteenth row of the letter
   [true, true, false, false, false, false, false, false, false, false, false, false, false, false, true, true],
   // The fourteenth row of the letter
   [true, true, false, false, false, false, false, false, false, false, false, false, false, false, true, true],
   // The fifteenth row of the letter
   [true, true, false, false, false, false, false, false, false, false, false, false, false, false, true, true],
   // The sixteenth row of the letter
   [true, true, false, false, false, false, false, false, false, false, false, false, false, false, true, true],
];

At first sight, this may look like much, but it really isn't. This 2D array is just like the grid above. But instead of '_' to represent "don't draw", we use the boolean false and instead of '1' to represent "draw", we use the boolean true.

Create a new file, font.rs in your src folder:

blasterball/
| .cargo/
| | config.toml
| src/
| | font.rs // NEW
| | main.rs
| .gitignore
| Cargo.lock
| Cargo.toml

In your font.rs, throw in the 'A' description above.

Now, let's take a look at the letter 'B':

0  - 1111111111______
1  - 11________11____
2  - 11_________11___
3  - 11_________11___
4  - 11_________11___
5  - 11________11____
6  - 1111111111______
7  - 11________11____
8  - 11__________11__
9  - 11___________11_
10 - 11___________11_
11 - 11___________11_
12 - 11___________11_
13 - 11__________11__
14 - 11________11____
15 - 1111111111______

In our font.rs file, this will translate to:

// Description of the letter 'B'
const B: [[bool; 16]; 16] = [
   // The first row of the letter
   [true, true, true, true, true, true, true, true, true, true, false, false, false, false, false, false],
   // The second row of the letter
   [true, true, false, false, false, false, false, false, false, false, true, true, false, false, false, false],
   // The third row of the letter
   [true, true, false, false, false, false, false, false, false, false, false, true, true, false, false, false],
   // The fourth row of the letter
   [true, true, false, false, false, false, false, false, false, false, false, true, true, false, false, false],
   // The fifth row of the letter
   [true, true, false, false, false, false, false, false, false, false, false, true, true, false, false, false],
   // The sixth row of the letter
   [true, true, false, false, false, false, false, false, false, false, true, true, false, false, false, false],
   // The seventh row of the letter
   [true, true, true, true, true, true, true, true, true, true, false, false, false, false, false, false],
   // The eighth row of the letter
   [true, true, false, false, false, false, false, false, false, false, true, true, false, false, false, false],
   // The ninth row of the letter
   [true, true, false, false, false, false, false, false, false, false, false, false, true, true, false, false],
   // The tenth row of the letter
   [true, true, false, false, false, false, false, false, false, false, false, false, false, true, true, false],
   // The eleventh row of the letter
   [true, true, false, false, false, false, false, false, false, false, false, false, false, true, true, false],
   // The twelfth row of the letter
   [true, true, false, false, false, false, false, false, false, false, false, false, false, true, true, false],
   // The thirteenth row of the letter
   [true, true, false, false, false, false, false, false, false, false, false, false, false, true, true, false],
   // The fourteenth row of the letter
   [true, true, false, false, false, false, false, false, false, false, false, false, true, true, false, false],
   // The fifteenth row of the letter
   [true, true, false, false, false, false, false, false, false, false, true, true, false, false, false, false],
   // The sixteenth row of the letter
   [true, true, true, true, true, true, true, true, true, true, false, false, false, false, false, false],
];

This is how things will generally be for the rest of the letters and symbols our font will support. First, you figure out where the pixels ought to be drawn so the resulting lines will look like the letter/symbol. You pin down this description in whatever way suits you, then you translate this description to Rust with our true/false representations.

Go ahead and do the rest for the letters 'C' - 'Z'.

Take Away

  • Drawing characters on screen boils down to describing how their lines are drawn with fonts, then drawing them.

Code till now:

Directory view:

blasterball/
| .cargo/
| | config.toml
| src/
| | font.rs
| | main.rs
| .gitignore
| Cargo.lock
| Cargo.toml

font.rs contents

// Description of the letter 'A'
const A: [[bool; 16]; 16] = [
   // The first row of the letter
   [false, false, false, false, false, false, false, true, true, false, false, false, false, false, false, false],
   // The second row of the letter
   [false, false, false, false, false, false, true, true, true, true, false, false, false, false, false, false],
   // The third row of the letter
   [false, false, false, false, false, true, true, false, false, true, true, false, false, false, false, false],
   // The fourth row of the letter
   [false, false, false, false, true, true, false, false, false, false, true, true, false, false, false, false],
   // The fifth row of the letter
   [false, false, false, true, true, false, false, false, false, false, false, true, true, false, false, false],
   // The sixth row of the letter
   [false, false, true, true, false, false, false, false, false, false, false, false, true, true, false, false],
   // The seventh row of the letter
   [false, true, true, false, false, false, false, false, false, false, false, false, false, true, true, false],
   // The eight row of the letter
   [true, true, false, false, false, false, false, false, false, false, false, false, false, false, true, true],
   // The ninth row of the letter
   [true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true]
   // The tenth row of the letter
   [true, true, false, false, false, false, false, false, false, false, false, false, false, false, true, true],
   // The eleventh row of the letter
   [true, true, false, false, false, false, false, false, false, false, false, false, false, false, true, true],
   // The twelfth row of the letter
   [true, true, false, false, false, false, false, false, false, false, false, false, false, false, true, true],
   // The thirteenth row of the letter
   [true, true, false, false, false, false, false, false, false, false, false, false, false, false, true, true],
   // The fourteenth row of the letter
   [true, true, false, false, false, false, false, false, false, false, false, false, false, false, true, true],
   // The fifteenth row of the letter
   [true, true, false, false, false, false, false, false, false, false, false, false, false, false, true, true],
   // The sixteenth row of the letter
   [true, true, false, false, false, false, false, false, false, false, false, false, false, false, true, true],
];

// Description of the letter 'B'
const B: [[bool; 16]; 16] = [
   // The first row of the letter
   [true, true, true, true, true, true, true, true, true, true, false, false, false, false, false, false],
   // The second row of the letter
   [true, true, false, false, false, false, false, false, false, false, true, true, false, false, false, false],
   // The third row of the letter
   [true, true, false, false, false, false, false, false, false, false, false, true, true, false, false, false],
   // The fourth row of the letter
   [true, true, false, false, false, false, false, false, false, false, false, true, true, false, false, false],
   // The fifth row of the letter
   [true, true, false, false, false, false, false, false, false, false, false, true, true, false, false, false],
   // The sixth row of the letter
   [true, true, false, false, false, false, false, false, false, false, true, true, false, false, false, false],
   // The seventh row of the letter
   [true, true, true, true, true, true, true, true, true, true, false, false, false, false, false, false],
   // The eighth row of the letter
   [true, true, false, false, false, false, false, false, false, false, true, true, false, false, false, false],
   // The ninth row of the letter
   [true, true, false, false, false, false, false, false, false, false, false, false, true, true, false, false],
   // The tenth row of the letter
   [true, true, false, false, false, false, false, false, false, false, false, false, false, true, true, false],
   // The eleventh row of the letter
   [true, true, false, false, false, false, false, false, false, false, false, false, false, true, true, false],
   // The twelfth row of the letter
   [true, true, false, false, false, false, false, false, false, false, false, false, false, true, true, false],
   // The thirteenth row of the letter
   [true, true, false, false, false, false, false, false, false, false, false, false, false, true, true, false],
   // The fourteenth row of the letter
   [true, true, false, false, false, false, false, false, false, false, false, false, true, true, false, false],
   // The fifteenth row of the letter
   [true, true, false, false, false, false, false, false, false, false, true, true, false, false, false, false],
   // The sixteenth row of the letter
   [true, true, true, true, true, true, true, true, true, true, false, false, false, false, false, false],
];

In the Next Post

We'll be drawing the characters in our font descriptions on the screen

References

  • https://en.wikipedia.org/wiki/Computer_font