// This file is part of www.nand2tetris.org // and the book "The Elements of Computing Systems" // by Nisan and Schocken, MIT Press. // File name: projects/12/Screen.jack /** * A library of functions for displaying graphics on the screen. * The Hack physical screen consists of 512 rows (indexed 0..511, top to bottom) * of 256 pixels each (indexed 0..255, left to right). The top left pixel on * the screen is indexed (0,0). */ class Screen { static boolean color; static Array powersOfTwo; // Helper for bit manipulation /** Initializes the Screen. */ function void init() { let color = true; // Default black let powersOfTwo = Array.new(16); let powersOfTwo[0] = 1; let powersOfTwo[1] = 2; let powersOfTwo[2] = 4; let powersOfTwo[3] = 8; let powersOfTwo[4] = 16; let powersOfTwo[5] = 32; let powersOfTwo[6] = 64; let powersOfTwo[7] = 128; let powersOfTwo[8] = 256; let powersOfTwo[9] = 512; let powersOfTwo[10] = 1024; let powersOfTwo[11] = 2048; let powersOfTwo[12] = 4096; let powersOfTwo[13] = 8192; let powersOfTwo[14] = 16384; let powersOfTwo[15] = 16384 + 16384; // 32768 (negative in 16-bit) return; } /** Erases the entire screen. */ function void clearScreen() { var int i; let i = 16384; while(i < 24576) { do Memory.poke(i, 0); let i = i + 1; } return; } /** Sets the current color, to be used for all subsequent drawXXX commands. * Black is represented by true, white by false. */ function void setColor(boolean b) { let color = b; return; } /** Draws the (x,y) pixel, using the current color. */ function void drawPixel(int x, int y) { var int address, value; var int mask; let address = 16384 + (y * 32) + (x / 16); let value = Memory.peek(address); // x & 15 is x % 16 let mask = powersOfTwo[x & 15]; if (color) { let value = value | mask; } else { let value = value & ~mask; } do Memory.poke(address, value); return; } /** Draws a line from pixel (x1,y1) to pixel (x2,y2), using the current color. */ function void drawLine(int x1, int y1, int x2, int y2) { var int dx, dy; var int a, b; var int diff; var int temp; if (x1 > x2) { let temp = x1; let x1 = x2; let x2 = temp; let temp = y1; let y1 = y2; let y2 = temp; } let dx = x2 - x1; let dy = y2 - y1; let a = 0; let b = 0; let diff = 0; // Vertical line if (dx = 0) { if (y1 > y2) { let temp = y1; let y1 = y2; let y2 = temp; } while (~(y1 > y2)) { do Screen.drawPixel(x1, y1); let y1 = y1 + 1; } return; } // Horizontal line if (dy = 0) { while (~(x1 > x2)) { do Screen.drawPixel(x1, y1); let x1 = x1 + 1; } return; } // Diagonal if (dy > 0) { while ((~(a > dx)) & (~(b > dy))) { do Screen.drawPixel(x1 + a, y1 + b); if (diff < 0) { let a = a + 1; let diff = diff + dy; } else { let b = b + 1; let diff = diff - dx; } } } else { while ((~(a > dx)) & (~(b < dy))) { do Screen.drawPixel(x1 + a, y1 + b); if (diff < 0) { let a = a + 1; let diff = diff - dy; // dy is negative } else { let b = b - 1; let diff = diff - dx; } } } return; } /** Draws a filled rectangle whose top left corner is (x1, y1) * and bottom right corner is (x2,y2), using the current color. */ function void drawRectangle(int x1, int y1, int x2, int y2) { var int r; let r = y1; while (~(r > y2)) { do Screen.drawLine(x1, r, x2, r); // reuse line for now for simplicity let r = r + 1; } return; } /** Draws a filled circle of radius r<=181 around (x,y), using the current color. */ function void drawCircle(int x, int y, int r) { var int dy; var int r2; var int halfWidth; if (r > 181) { return; // overflow check } let dy = -r; let r2 = r*r; while (~(dy > r)) { let halfWidth = Math.sqrt(r2 - (dy*dy)); do Screen.drawLine(x - halfWidth, y + dy, x + halfWidth, y + dy); let dy = dy + 1; } return; } }