nand2tetris - Project 12

Screen.jack implementation!

This took me a while!
// 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).
 */
 
// Screen starts at 16384
// 8K screen memory map
// 512 x 256
// each 
class Screen {
 
    static Array screen;
    static boolean isBlack;
    static Array bitArray;
 
    /** Initializes the Screen. */
    function void init() {
        let bitArray = Array.new(16);
        let bitArray[0] = 1;                // 0000000000000001
        let bitArray[1] = 2;                // 0000000000000010
        let bitArray[2] = 4;                // 0000000000000100
        let bitArray[3] = 8;                // 0000000000001000
        let bitArray[4] = 16;               // 0000000000010000
        let bitArray[5] = 32;               // 0000000000100000
        let bitArray[6] = 64;               // 0000000001000000
        let bitArray[7] = 128;              // 0000000010000000
        let bitArray[8] = 256;              // 0000000100000000
        let bitArray[9] = 512;              // 0000001000000000
        let bitArray[10] = 1024;            // 0000010000000000
        let bitArray[11] = 2048;            // 0000100000000000
        let bitArray[12] = 4096;            // 0001000000000000
        let bitArray[13] = 8192;            // 0010000000000000
        let bitArray[14] = 16384;           // 0100000000000000
        let bitArray[15] = 16384 + 16384;   // 1000000000000000
 
        let screen = 16384;
        let isBlack = true;
        return;
    }
 
    /** Erases the entire screen. */
    function void clearScreen() {
        var int i;
        let i = 0;
        while (i < 8192) {
            let screen[i] = false;
            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 isBlack = b;
        return;
    }
 
 
    /** Draws the (x,y) pixel, using the current color. */
    function void drawPixel(int x, int y) {
        var int address, value, modulo, pattern;
 
        let address = (32 * y) + (x / 16);
        let value = Memory.peek(16384 + address);
 
        let modulo = x - (x / 16 * 16); // a % b can be done as a-(a/b*b)
 
        let pattern = bitArray[modulo];
 
        if (isBlack) {
            let value = value | pattern;
        } else {
            let pattern = ~pattern;
            let value = value & pattern;
        }
 
        do Memory.poke((16384 + address), value);
        return;
    }
 
    /** Draws a line from (x1, y1) to (x2, y2). */
    function void drawLine(int x1, int y1, int x2, int y2) {
        var int dx, dy, a, b, diff;
 
        if (y1 = y2) {
            do Screen.drawHLine(x1, x2, y1);
            return;
        }
 
        if (x1 = x2) {
            do Screen.drawVLine(x1, y1, y2);
            return;
        }
 
        let dx = x2 - x1; 
        let dy = y2 - y1; 
 
        let a = 0;
        let b = 0;
        let diff = 0;
 
 
        if (dx > 0 & 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;
                }
 
            }
        }
 
        if (dx < 0 & 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;
                }
 
            }
        }
 
        if (dx > 0 & 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;
                }
 
            }
        }
 
        if (dx < 0 & 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;
                }
 
            }
        }
 
 
        return;
    }
 
    //draw horizontal line
    function void drawHLine(int x1, int x2, int y) {
        var int temp;
        var int diff;
 
        if (x1 > x2) {
            let temp = x1;
            let x1 = x2;
            let x2 = temp;
        }
 
        let diff = x2 - x1;
 
        while (diff > -1) {
            do Screen.drawPixel((x1 + diff), y);
            let diff = diff - 1;
        }
 
 
        return;
    }
 
    //draw vertical line
    function void drawVLine(int x, int y1, int y2) {
        var int temp;
        var int diff;
 
        if (y1 > y2) {
            let temp = y1;
            let y1 = y2;
            let y2 = temp;
        }
 
        let diff = y2 - y1;
 
        while (diff > -1) {
            do Screen.drawPixel(x, y1 + diff);
            let diff = diff - 1;
        }
 
        return;
    }
 
    /** Draws a filled rectangle where the top left corner
     *  is (x1, y1) and the bottom right corner is (x2, y2). */
    function void drawRectangle(int x1, int y1, int x2, int y2) {
        while(~(y1 > y2)) {
            do Screen.drawHLine(x1, x2, y1);
            let y1 = y1 + 1;
        }
        return;
    }
 
    /** Draws a filled circle of radius r around (cx, cy). */
    function void drawCircle(int cx, int cy, int r) {
        var int dx, dy;
        var int r_squared;
 
        let dy = -r;
        let r_squared = r*r;
        while( ~(dy > r) ) {
            let dx = Math.sqrt(r_squared-(dy*dy));
            do Screen.drawHLine( cx-dx, cx+dx, cy+dy );
            let dy = dy + 1;
        }
 
        return;
    }
}