You have several cases for the rectangles. We only need to describe how to process one horizontal line. Let's first think of lines, which occupy 2 words. On the left you have: XXXX11111 (16 bits in total), or XXXX0000, where X are bits which need to remain the same, and 0 and 1 are bits that need to be set to 0 or 1 respectively. On the right you have that mirrored: 11111XXX or 00000XXX. (Of course, the number of zeroes, ones and Xes may vary for different rectangles.)
You will need to fetch the left word from the display memory, change the specified bits to ones or zeroes and put the new value back to the display memory. The same for the right one. How can you do the bit change? Would it be helpful if you have an array with the following binary values:
0000000000000000
0000000000000001
0000000000000011
0000000000000111
...
1111111111111111
Hint: Operations like AND, OR and NOT are quite fast, because they work on whole words.
If the line is more than 2 words long, the middle words are all just ones or just zeros, so you only need to write them to memory.
That leaves us with lines contained in a single word. Let's say that we need to create the following line: XXX111XXXXXXXXXX. We again need to read the value, set the several of the bits to 1 without changing the rest and put the new value back. It would be helpful if we had the following value available:
0001110000000000
The problem is, that it would only work for this rectangle. What if we needed it to begin on the next pixel? We would have to have this value then:
0000111000000000
And we would need different values for 1 pixels wide, 2 pixels wide, etc. So having these masks precomputed and stored into memory doesn't seem optimal. But there is a fast way to compute these values on the fly. You already have these:
0000001111111111
0001111111111111
How can you get this:
0001110000000000
P.S. You can implement this in a function called drawHLine, which you can use in drawRectangle and drawCircle to speed them up. You can use it in drawLine to draw the horizontal parts of each line as well.
P.P.S. I hope I gave you enough hints without spoiling the fun of the a-ha! moment. I'm sorry if I failed.
The main thread for discussing code optimizations is
Generated code size. There are a few good ideas there, like call and return optimizations, dead code elimination, etc.
If you want to diverge from the book, you can check out register machines as alternative to the stack-based VM. You can check the
Some fun with SSA thread for . SSA stands for
Static Single Assignment -- a code transformation, which enables a wide range of optimizations. It is used in LLVM, GCC and many more compilers.