// 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; /** Initializes the Screen. */ function void init() { let color = true; return; } /** Erases the entire screen. */ function void clearScreen() { var int i; var Array a; let i = 0; let a = 16384; while (i < 8192) { let a[i] = 0; let i = i + 1; } return; } function int modulo(int a, int b) { var int d; let d = a / b; return a - (d * b); } /** 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; var int value; var int i; var int mask; var int inverseMask; var int pixel; let pixel = Screen.modulo(x, 16); let i = 0; let mask = 1; while (~(i = pixel)) { let mask = mask + mask; let i = i + 1; } let inverseMask = ~mask; let address = 16384 + (32 * y) + (x / 16); let value = Memory.peek(address); if (color) { let value = ((value & inverseMask) | ((-1) & mask)); // pixel gets set to 1 } else { let value = (value & inverseMask); // pixel gets set to 0 } 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 a, b, diff, dx, dy, xinc, yinc; var boolean continue; let a = 0; let b = 0; let diff = 0; let dx = Math.abs(x2 - x1); let dy = Math.abs(y2 - y1); let continue = true; if (x2 > x1) { let xinc = 1; // going from left to right; } else { let xinc = -1; // going from right to left; } if (y2 > y1) { let yinc = 1; // going from top to bottom } else { let yinc = -1; // going from bottom to top } // vertical line if (dx = 0) { while (~(y1 = y2)) { do Screen.drawPixel(x1, y1); let y1 = y1 + yinc; } return; } // horizontal line if (dy = 0) { while (~(x1 = x2)) { do Screen.drawPixel(x1, y1); let x1 = x1 + xinc; } return; } while (continue) { do Screen.drawPixel(x1 + a, y1 + b); if (diff < 0) { let a = a + xinc; let diff = diff + dy; } else { let b = b + yinc; let diff = diff - dx; } if ((x1 + a) = x2) { if ((y1 + b) = y2) { let continue = false; } } } 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 yinc; if (y2 > y1) { let yinc = 1; } else { let yinc = -1; } while (~(y1 = y2)) { do Screen.drawLine(x1, y1, x2, y1); let y1 = y1 + yinc; } 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 halfSegment; let dy = -r; if (r > 181) { return; } while (dy < (r + 1)) { let halfSegment = Math.sqrt((r * r) - (dy * dy)); do Screen.drawLine(x - halfSegment, y + dy, x + halfSegment, y + dy); let dy = dy + 1; } return; } }