Screen Memory

Well, we've explained the first few lines of put(), but there are some more. The other function which it performs is to display something on the screen when someone writes to screen memory. Recall that in our fictitious machine, a large chunk of memory from 0xA000 onwards, is designated as screen memory.

The way screen memory works is that each pair of locations stands for a single character location on the screen. The screen has 80x40 characters (i.e. 40 rows-na of 80 characters each). The reason there are two bytes for each screen location is that the first contains an attribute byte and the second contains the ASCII code of the character itself. The easiest way of figuring out what an ASCII code is, if you don't know, is to do a web search and find an ASCII table. But needless to say, each kind of character that can be displayed on the screen (in textmode) has a code associated with it. E.g. the character A has a code of 65 in decimal. The attribute byte specifies what the foreground and background colours of that individual character ought to be.

Now there is one slight problem with just stashing data in screen memory at any location. It might be that the user tries to stash data at an odd memory location. But this would overwrite the ASCII data of one screen location and the attribute data of the next screen location. To prevent this scenario (which would require a lot of fiddly code to handle properly anyway) we simply check to make sure that an even address has been specified if we are writing to screen memory, and ignore the request otherwise. This is not an ideal way of doing things, but it makes for slightly more predictable results.

The first thing our function put() actually does, therefore, is to check whether the location being written to is screen memory (there are 80x40x2 bytes of data allocated to screen memory). It also checks that an even address has been supplied. This is easy to check, since we just need to know the remainder after dividing the address by 2. If there was 0 remainder, then the address was even, otherwise it was odd.

The next four lines consist of non-standard C functions which are available with Pelles C version 4.0 and above. These are the actual functions we use to write to the screen in the real world. So, a request to write to the screen memory in our fictitious machine, translates into these four C functions which actually write to the screen in the real machine.

The first function sends the cursor to a particular location on the screen, the second sets the background colour at the cursor location, the third sets the text colour at that location and the fourth writes the actual character, given its ASCII value.

In order to figure out all the data which needs to be sent to these four functions, we again invest in a bit of mathematics. We first subtract 0xA000 from the memory address, to see how far into screen memory it is. Since it is even, we then divide by two, and this returns the character position. But then we need to figure out which row and column this corresponds to on the screen. Again a little modular arithmetic suffices. We want the quotient and remainder upon division by 80. Because real screen columns and rows-na are numbered from 1 to 80 and 1 to 40 respectively, instead of numbering them from 0, we need to add 1 to each of the results we get.

To figure out the text colour and background colour we need to get each of the 4 bit halves of the attribute byte which is inside our word of data fetched from our fictitious memory. We can obtain these by shifting the data about and masking off bits we don't want to see. We first use the >> operator, which shifts the data the specified number of bits to the right. Since we only want to see the top 4 bits of our 16 bit word, we shift it 12 bits to the right. To see the next 4 bits down, we start with the same original word and instead, shift it by 8 bits. But then we need to mask off the top 4 bits which are still present. This we do with the logical AND operation, &, which has the effect of masking off any bits ANDed with a zero. Since we only want the lowest 4 bits, we AND with the word 0000 0000 0000 1111 in binary, or 0x000F in hexadecimal. Again, to retrieve the lowest 8 bits of our word (corresponding to the ASCII value of our character), no shifting is necessary, but we AND with 0x00FF which allows-na only the bottom 8 bits to show, masking off the other 8 bits by ANDing them with zeroes in binary.

Note that the C functions for displaying characters on the screen are included in the library conio.h, so we have included that at the beginning of our program.

For now we will ignore all the #defines at the beginning of our program and the function setflags() and simply look at the main procedure in emulate.c, namely emulate(). This will be done in our next lesson.