BigBang wrote
I have a little confusion. In the video course (Module 4.5), a graphic of the display unit is shown (at minute 5:15). This map starts counting from column 0 - 511.
The calculation "col % 16" is used to determine the bit.
If I want to select the first pixel in a row (column 0) and enter a 0 for col in the calculation "col % 16", the result tells me that I should select the 0th bit. There is no bit 0 in a RAM register.
But there is a word 0 and a row 0.
For example, the first pixel in the top left corner cannot be selected with row 0 and col 0. Instead, row 0 and col 1 must be selected.
Example A (row = 0, col = 0)
word: 0 x 32 + 0 / 16 = 0
bit: 0 % 16 = 0
Example B (row = 0, col = 1)
word: 0 x 32 + 1 / 16 = 0
bit: 1 % 16 = 1
So that the calculation "row x 32 + col / 16" works, there are rows 0 - 255.
But the columns in the calculation "row x 32 + col / 16" and "col % 16" are given from 1-512? Is that correct?
Each word in RAM consists of 16 bits, and therefore can be used to store the state of 16 pixels in the screen, such each pixel can only have one of two values.
The screen is 512 columns by 256 rows, for a total of 131,072 pixels. With 16 pixels per word, that means that we can store the state of every pixel in the screen using 8,192 words of RAM (which is 8K words). The Hack platform maps an 8K block of RAM, starting at address 16,384, to the screen. The first word is mapped to the first 16 pixels, the second word to the next 16, and so on. The first 32 words in the screen RAM is used to encode the first row (since 32 words * 16 pixels/word = 256 pixels), the next 32 words are the next row, and so on.
In selecting a particular pixel in RAM, given its row (0-255) and column (0-511), we need to first identify the address of which word in RAM it is stored in. Then, once we know the address in RAM, we need to identify which of the 16 bits within that RAM location is the one for the specific pixel we are interested in.
There are several ways to come at the math, so let's try the following:
Let's number (index) the pixels 0 through 131,071 (i.e., let's forget about RAM and how many pixels are in a RAM cell for the moment), with the top left being pixel 0, the one to the right of it being pixel 1, and so on across the top row. That makes the last pixel on that row pixel 511 and the first pixel on the second row pixel 512. We keep associating pixel locations with pixel indices this way until the bottom-right pixel is assigned index 131,071.
If I number the rows 0-255 and the cols 0-511, then I can calculate the pixel index (P) from the row (R) and column (C) values as
P = 512 * R + C
The value of R tells me how many complete rows of 512 pixels lie above row R, while the value of C tells me how many pixels lie to the left of column C on the row R.
Notice that this only works if we number things starting with 0. That's everything, R, C, and P all start counting from 0.
Now, once I know the index of P, I can easily put the pixels into groups of B pixels each and number the groups starting with 0.
Let's take an aside and imagine that I have a bunch of parts that are serialized, starting with serial number 0, and I put 20 of them in a box, with S/N 0 on the left and S/N 19 on the right. I number this box Box 0 and put it on a shelf. I put the next 20 items in Box 1, and so on.
Sometime later, someone comes along and want the item with S/N 4271. Which box is it in? If I divide 4271 by 20, I get 213.55. This tells me that there are 213 full boxes before the one that contains the one I want and that the one I want is in the 214th box. But because I numbered the boxes starting with 0, the number written on it is actually 213. So I can find the box number just by dividing the serial number by 20 and throwing away any fractional part.
But what about the part's position within Box 213?
Well, I can get that by going back to the first kind of division I learned, back before I knew about decimal points or even fractions. Back then, if asked to divide 4271 by 20, I would have written the answer as
4271 / 20 = 213 r 11
The integer quotient is the box number, and the remainder is the position within the box (assuming that we start counting with 0 as the first box and the first position within a box).
In the Hack (and most programming languages), when you divide one integer by another, the quotient that is returned is the integer quotient, which is exactly what we want. We also have the remainder operator, %, which returns the remainder after integer division, which again is exactly what we want. So
box = (serial_number) / (items_per_box)
position = (serial_number) % (items_per_box)
We can use this same approach to put out pixels into groups of 16 pixels in a RAM word.
ram_offset = P / 16
bit_offset = P % 16
The ram_offset is how may RAM cells the desired cell is beyond the beginning of the screen memory. So if ram_offset turns out to be 0, that means that the RAM address we want is SCREEN (i.e., 16,384).
ram_address = SCREEN + P/16
Now we just need to replace P with the expression for it in terms of R and C and then simplify.
ram_address = SCREEN + ((512*R + C) / 16)
bit_offset = (512*R + C) % 16
ram_address = SCREEN + (512*R/16) + (C/16)
ram_address = SCREEN + (32*R) + (C/16)
bit_offset = (512*R)%16 + C%16
bit_offset = C % 16
The simplification of the bit_offset comes about because 512 is evenly divisible by 16, and therefore so is any integer multiple of it. Hence, when we divide 512*R by 16, we are guaranteed to get a remainder of zero, regardless of what R happens to be.
Now, within a RAM location, we need to number the sixteen bits 0 through 15 (since we need to start ALL counting at zero for the math to work), but we are free to do that in any way we want. The designers of the Hack I/O mapping chose to associate the least-significant bit with the left-most pixel (of that group of sixteen) and the most-significant bit with the right-most pixel. That may seem counter-intuitive since we normally write the least-significant digit of a number on the right, but it actually makes the instruction sequence needed to access the desired bit, particular in the limited Hack assembly language, a LOT simpler.