// 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 256 rows (indexed 0..255, top to bottom) * of 512 pixels each (indexed 0..511, left to right). The top left pixel on * the screen is indexed (0,0). */ class Screen { static boolean black; /** Initializes the Screen. */ function void init() { //デフォルトは黒 let black = true; return; } /** Erases the entire screen. */ function void clearScreen() { var int poi; let poi = 16384; //スクリーンのメモリマップ[16384-24575]. 512 x 256ドットの白黒スクリーン while(poi < 24576) { do Memory.poke(poi, 0); let poi = poi + 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 black = b; return; } /** ビット演算使うバージョン */ function void drawPixel(int x, int y) { var int poi, mod, val, i, j; let poi = 16384 + (x / 16) + (y * 32); let mod = x & 15; let val = Memory.peek(poi); //modビット目だけ黒にする let i = 0; let j = 1; while(i < mod) { let j = j + j; let i = i + 1; } if(black) { //modビット目だけ黒にする let val = val | j; } else { //modビット目だけ白にする let val = val & (~j); } do Memory.poke(poi, val); return; } /** Draws the (x,y) pixel, using the current color. */ /** ビット演算なしバージョン */ function void drawPixel_old(int x, int y) { var int poi, mod, val, tmp, i, j; let poi = 16384 + (x / 16) + (y * 32); let mod = x - (x / 16 * 16); let val = Memory.peek(poi); //-1 => 32767, -2 => 32766, -n => 32768 + n let tmp = val; if(tmp < 0) { //上一桁目(16ビット目)のビットを0にする let tmp = 32767 + val + 1; } //intの範囲は-32768から+32767, tmpの15ビット目は必ず0になっている //n + 1桁目のビットを0にする方法(""の中は該当ビットを1(black)にする場合) //tmp / 2 * 2 "+ 1" ... n=0 //tmp / 4 * 4 + (tmp - (tmp / 2 * 2)) "+ 2" ... n=1 //tmp / 8 * 8 + (tmp - (tmp / 4 * 4)) "+ 4" ... n=2 //... //tmp / 16384 * 16384 + (tmp - (tmp / 8192 * 8192)) "+ 8192" ... n=13 //if(tmp > 16383) tmp - 16384 "+ 16384" else tmp "+ 16384" ... n=14 //上記(n=0~n=14)についてvalがマイナスの場合は15ビット目のビットを立てる必要がある。 //tmp "15ビット目のビットを立てる"... n=15 // //15ビット目のビットを立てる方法 //32767 => -1, 32766 => -2, n => n - 32767 - 1 if(mod = 15) { if(black) { let tmp = tmp - 32767 - 1; } } else { if(mod = 14) { if(tmp > 16383) { let tmp = tmp - 16384; } if(black) { let tmp = tmp + 16384; } } else { let i = 0; let j = 1; while(i < mod) { let j = j + j; let i = i + 1; } let tmp = ((tmp / (j + j)) * (j + j)) + (tmp - (tmp / j * j)); if(black) { let tmp = tmp + j; } } if(val < 0) { let tmp = tmp - 32767 - 1; } } do Memory.poke(poi, tmp); 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 tmp, a, b, dx, dy, adyMinusbdx; if(x1 > x2) { let tmp = x1; let x1 = x2; let x2 = tmp; let tmp = y1; let y1 = y2; let y2 = tmp; } let dx = x2 - x1 + 1; let dy = y2 - y1 + 1; let a = 0; if(dy = 1) { while(a < dx) { do Screen.drawPixel(x1 + a, y1); let a = a + 1; } return; } let b = 0; if(dx = 1) { if(y1 > y2) { let tmp = y1; let y1 = y2; let y2 = tmp; let dy = -dy; } while(b < dy) { do Screen.drawPixel(x1, y1 + b); let b = b + 1; } return; } let adyMinusbdx = 0; if(dy > 0) { while((a < dx) & (b < dy)) { do Screen.drawPixel(x1 + a, y1 + b); if(adyMinusbdx < 0) { let a = a + 1; let adyMinusbdx = adyMinusbdx + dy; } else { let b = b + 1; let adyMinusbdx = adyMinusbdx - dx; } } return; } else { while((a < dx) & (b > dy)) { do Screen.drawPixel(x1 + a, y1 + b); if(adyMinusbdx < 0) { let a = a + 1; let adyMinusbdx = adyMinusbdx - dy; } else { let b = b - 1; let adyMinusbdx = adyMinusbdx - 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 tmp; if(x1 > x2) { let tmp = x1; let x1 = x2; let x2 = tmp; } if(y1 > y2) { let tmp = y1; let y1 = y2; let y2 = tmp; } let x2 = x2 + 1; let y2 = y2 + 1; while(y1 < y2) { let tmp = x1; while(tmp < x2) { do Screen.drawPixel(tmp, y1); let tmp = tmp + 1; } let y1 = y1 + 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 dx, dy; let r = Math.abs(r); //p301より if(r > 181) { do Sys.error(21); } let dy = -r; //<=に対応するため let r = r + 1; while(dy < r) { //sqrtは若干重いが足し算だけで計算するアルゴリズムがあるらしい(p292) let dx = Math.sqrt((r * r) - (dy * dy)); do Screen.drawLine(x - dx, y + dy, x + dx, y + dy); //円の外周だけ描く場合 //do Screen.drawPixel(x - dx, y + dy); //do Screen.drawPixel(x + dx, y + dy); let dy = dy + 1; } return; } }