In the previous unit we showed you the Square Dance application. And the Square Dance involved a very simple graphics image, a square, and in fact we use the operating system in order to draw the square. In many applications we have to use some of fancy graphics, some less regular polygons than the squares and rectangles. So, what do you do when you have to create high performance graphics? Well, you can do some tricks that we're going to unfold in this unit. So, we saw Square Dance, and you may had the chance to look at the Space Invaders app. You can look it up in the course website. And if you look closer at the images from which Space Invaders is made, you will see these various creatures here, so we see the alien, and the shooter, and the shots. If you look at another application Sokoban, once again if you focus on the graphical building blocks of this application, you will see that this maze is made up from our repeating patterns of some basic images, as you see here at the bottom. And by and large, all these images are typically referred to as sprites. So a sprite is a two-dimensional bitmap, that typically serves as a singular image, or pitting image in a larger scene. And as application developers, we have to worry about two things. First of all, how to grow sprites quickly, which is not obvious when the sprite is an irregular shape that does not lend itself to standard drawing routines. And in a similar fashion, we have to worry about moving these sprites around in a smooth and acceptable way to the human eye, to the eye of the user. Now, there are two basic ways to do it. One is the standard approach, which uses the host operating system. And yet, at some point, the capabilities of the OS can kind of run out of steam, and then you have to use some customized code that you may want to develop by yourself. And that's what this unit is all about, how to bypass the operating system and do your graphics using your own skills. All right. So. The graphics that you're going to create in GECK at the end of the game, is going to execute on the Hack computer. And the Hack computer, if you recall, features a 32K RAM. And as you may recall from project zero, the computer is organizing in such a way that there is an 8K add respace, which is dedicated to the screen memory map. And there's a single word, memory map, which is dedicated to the keyboard. And to these two memory maps, we can connect a screen and the keyboard. And the computer also features and ongoing refresh cycle that refreshes the physical screen from the contents of the screen memory map. So, if you want to draw something on the screen, you have to write into the Hack RAM. Now, let us take a closer look at the screen, because that's the focus of this unit, graphics on the Hack screen. So the screen is a grid that consist of 256 rows of 512 pixels each, and if I want to draw three pixels somewhere on the screen, well in that case, I have to write the number seven or 111 somewhere in the screen memory map. And I've chosen some arbitrary address 19,003 that presumably corresponds to what we see here on the screen. If we want to see some more pixels, let say 16 pixels, then we have to go to the same address and write the number minus one, which is binary is 16 ones to its compliment. And so as you see, whenever you want to render something on the screen, the only way to do it is to write something into the RAM, into the screen memory map, under program control. If you're writing in a machine language, you can do it directly. If you write in GECK, then you have to do it using GECK commands. So, how can we do it using GECK commands? Well, first of all, we are lucky to have class called memory, as part of our operating system. And in the memory class there are two functions called pick and poke, which enable me to do what I just described. I can use pick and poke in order to select certain addresses in the memory, and either fetch the value which is lodged into this address, or we can change this value by using poke. So here are some examples. If I will write the code let x equals peak of this address, 19,003, x will be set to 7, which is the decimal value of the binary, 111. If alternatively I will say, poke in the same address, minus 1, then this word will not be a 1 anymore. I'm sorry, it will not be 7 anymore. It will become minus 1, which is 16 ones in binary. So, I can control the RAM using these two operations. So, that's one way of doing it. Let me show you another way. I can also draw pixels using the operating system. So for example, if I want to draw this rectangle that you see here, which consists of let's see, two rows of three pixels each, then there are several ways of doing it. And I can do it using the screen class OS features. So let's see, we have draw pixel, draw line, draw rectangle. So we can, first of all we have to decide or to make an assumption about the location of the desired rectangle. So let us decide that these are the coordinates of the top left corner of this image. So, one thing that you can do is simply draw all the pixels that make up this image. So we have six pixels, and we draw them one at a time. Another thing that we can do, is we can use the draw line routine in order to draw two lines or three pixels each. And I can also draw this image using the draw rectangle routine, that given the parameters that I'd supplied, will draw the same image. So any one of these techniques here will do the same thing. Now, drawing regular figures like this, is kind of easy, isn't it? What about images that look like that? Well, this is an regular image, and we have no OS routine which is specifically designed to draw anything like that. And if we had such a routine, I could come up as a devil's advocate, I could come up with another example that you cannot draw, and so on and so forth. So at some point, we have to deal with irregular figures. And one way to deal with these figures is to simply draw all the pixels, right? So, I can figure out all the coordinates of all these pixels, and for each one of these pixels, draw it, one at a time. And it turns out that in this particular example, I will end up using 75 different pixel drawing operations. Now, when you think about efficiency, you should never think about high level, well you can begin to think about high level, but at some point you should always think about the low level. How long will it actually take to draw this image on the ultimate host platform? Well, it turns out that every one of these pixels at the low level, will require all the operations that you see here. None of which is trivial, okay? So, I've written it in Pseudocode, that the operating system will have to implement all these, and at some point will have to translate it into machine language and so on. And so when you look at this batch of commands here, and if you're worried about efficiency, then the only two words that come to my mind are oy vey. Because, what we have here, I think is something around at least 40, 4 O machine operations or machine cycles. And so, the efficiently of this operation is 40 times 75, which is around 3,000 machine operations, a lot of work to draw this little sprite on the screen. And think about animation. We have to draw this image thousands of times within a loop. All right, so there must be a better way. And indeed there is a better way, which we call custom drawing. So, as you may have imagined, one thing that we can do is look at this image here, and realize that we are actually looking at a bunch of numbers. Right? Binary numbers that represent these pixels. So, we can render this image on the screen by simply writing a bunch of numbers, and these are the numbers in decimal, into selected, carefully selected addresses in the RAM, and then we'll get exactly the same effect. And so, in order to write these numbers, it turns out that I need 16 poke operations. And so the efficiency will be 16 poke operations. Now, poke is a highly optimized operation, because it's a 16-bit operation which is very natural to the host platform. And so it can be implemented with, I believe four machine operations. So we have 4 times 16 which is 64 operations, compared to the 3,000 that we had before, significant improvement in terms of efficiency. All right, so we can continue to talk about custom drawing, and now I'd like you to note that that we never draw these sprites in isolation. They are always located in a certain location on the host screen. So let us not forget that we have to decide about the exact addresses of these values. And that's what I want to do in the next few minutes, to discuss the addressing scheme. So, first of all we call that what we see here on the screen is a rendition, an ongoing rendition which is refreshed all the time, of the 8K screen memory map on the host RAM. So let us focus only on the memory map, and let us renumber the renumber of the memory map from zero to 8K. And let us remember also the coordinates of the top left corner of the sprite. And I've filled in some bits into the memory map that illustrate what we actually see in the memory, when we see this stripe on the screen. So let's see, the first row of the screen which is 512 bits of pixels, is represented by the first 32 words of the RAM, and they're all zeroes because nothing is drawn there. And here we see the 32 words that represent the next row, or the next 512 pixels there if you will, from which the screen is made. And then at some point we'll get to the address where the sprite actually begins. And we see that we have a bunch of zeros, and a bunch of ones, and the few more zeros. Well, you see these are the bits that represents the pixels, that show the top hair line of the avatar, okay? So, these ones are the hairs that stand on his or her top of the head. And then the next 32 words represent the next row of pixels in this image. And as you see we have a bunch of zeros, then we have two ones, more zeros, two ones, more zeros. Well, these ones are the edges of the head of the avatar, the hair that the avatar has here, and the lack of the hair that he has in the middle is the zeros. And it goes on and on, and at some point, we get to the bottom row of the avatar, or this bitmap, and here we see the bottom. Outline of the shoes of this figure with a single zero pixel in the middle to separate the two shoes from each other. So, what you see here is the mapping between the two dimensional screen on the right hand side and the vector of 16 bit words from which the memory of the host platform is made. All right now therefore with that in mind if we want to draw this bitmap we have to enter, as I said before, a bunch of 16 poke operations, with carefully selected the addresses. Everything begins with a certain constant addr. And addr is the first word that we want to draw in the RAM. Now, if I want to turn this into absolute addressing, then I have to add to addr, first of all, the base address of the screen memory map in the host RAM, which is 16,384. And then I also have to add to it the location on the screen, where I want to or fill the computer location that corresponds to the point where I want to start to draw this avatar on the screen. Once I will add up these two constants I will have the 16 operation that will end up drawing the avatar on this exact location on the screen. Now, you may agree with me that all this accounting is quite disturbing and tedious. And therefore you will be very happy, I think, to realize that we provide a tool that does all this automatically for you. We have a bitmap editor provided to you courtesy of Golan Parashi, who is one of the students who took this course. And it's a very nice little JavaScript application which is made available to you. And all you have to do is invoke this application, it will start running in your browser, you will get to see a 16 by 16 grid. You can the use your mouse to turn on and off individual pixels. And then you hit a button in the middle of the screen, and the application will automatically generate all the poke commands which are necessary to render the image of your choice on the hex screen. So from this point onward, all you have to do is copy this code from the browser and paste it into your Jack Code and voila, you have customized code that controls your graphics directly and bypassing the operating system. All right, now if you felt somewhat lost about the details of the host platform. Then you're welcome to look again at project zero in which we discussed the head computer as well as input and output operations. But perhaps you don't have to spend too much time on this because this little tool here really does most of the work for you. But of course if you want to get more background you're welcome to do it. So I'd like to end with some best practice advice on using high performance graphics. And my best advice is that, in many cases, you don't use it at all. If all you want to do is use some simple graphics, you should use the standard OS services. If you want to do something more fancy, you can also begin with the standard OS services. And only if something is not working to your satisfaction, only then, you can opt to do the fancy tricks that we did in this unit. So, in particular, if you want to do high-performance graphical animation, you can do what we showed here. And if you're willing to settle for 16 by 16 pixel sprites, you're welcome to use the bitmap editor, and with all these, you are free to now develop fancy applications. And hopefully the games that you come up with, or whatever you want to do, will be cool and exciting. All right, so this has been the unit in which we talked about high performance graphics. And in the next unit we will finally discuss project 9 which is the Jack program that you have to write as the capstone project of this module.