计算机系统要素:第十二章 操作系统

简介:

最终来到了Hack计算机架构的最后一部分——操作系统的构建了。这一章的内容涉及了大量的逻辑架构、算法问题与细节处理,须要花非常多精力才可以完毕。我以前与nand2tetris团队的一位工作人员有过联系,他就指出,这本书最后几个较难的章节介绍性的内容太少了。最后的OS章节假设作为正常的上课来学习的话,两周时间是绝对不够的(仅仅要想想,计算机专业的学生得花一个学期学OS)。当中涉及了太多问题。

因此。在这篇文章中。我会将语言规范、溢出处理、特殊情况处理等几个重要的问题单独列出加以讨论。

 

Math

溢出(overflow)处理:

Math中各个算法的实现事实上都不难,重要的是必须考虑溢出情况。由于这是一台16位的计算机。所以它能表示的数字范围在-32768~32767之间。

而溢出情况就是指计算的数字超出了这个范围,假设不加以考虑的话。溢出部分的数字会出现混乱。这里,我给大家提供一篇參考文章。里面具体阐述了这方面的内容。

// 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/Math.jack

/**
 * A basic math library.
 */
class Math {

    /** Initializes the library. */
    function void init() {
		return;
    }

    /** Returns the absolute value of x. */
    function int abs(int x) {
		var int absNum;
		if (x < 0){
			let absNum = -x;
		}
		else{
			let absNum = x; 
		}
		return absNum;
    }

    /** Returns the product of x and y. */
    function int multiply(int x, int y) {
		var int sum;
		var int shiftedX,functionY;
		var int flag,j;
		var boolean WhetherNeg;
		let sum = 0;
		let shiftedX = Math.abs(x);
		let functionY= Math.abs(y);
		let flag=1;
		let j=0;
		if ((x=0)|(y=0)){
			return 0;
		}
		let WhetherNeg = ((x<0)=(y<0));
		while(j<16){
			if(functionY&flag=flag){
				let sum = sum + shiftedX;
			}
			let shiftedX=shiftedX+shiftedX;
			let flag=flag+flag;
			let j=j+1;
		}
		if (~WhetherNeg){
		let sum=-sum;
		}
		return sum;
	}

    /** Returns the integer part of x/y (x>0,y>0). */
	function int div(int x, int y) {
		var int q,qy;
		if((y<0)|(y>x)){
			return 0;
		}
		let q = Math.div(x,y+y);
		let qy = Math.multiply(q,y);
		if (x-qy-qy<y){
			return q+q;
		}
		else{
			return q+q+1;
		}
	}
	
    /** Returns the integer part of x/y. */
	function int divide(int x, int y) {
		var int answer;
		var int absX,absY;
		var boolean WhetherNeg;
		let absX = Math.abs(x);
		let absY= Math.abs(y);
		if(absY=0){
			return Sys.error(3);
		}
		let WhetherNeg = ((x<0)=(y<0));
		let answer = Math.div(absX, absY);
		if (~WhetherNeg){
			let answer=-answer;
		}
		return answer;
	}

	/** Returns to the exponent number n where x <= 2^n. */
	function int logTwo(int x){
		var int powerTwo,flag;
		if ((x>16384)&((x<32767)|(x=32767))){
			return 15;
		}
		let powerTwo = 1;
		let flag = 0;
		while (powerTwo<x){
			let powerTwo = powerTwo+powerTwo;
			let flag = flag + 1;
		}
		return flag;
	}
	
	/** Returns to x^y. */
	function int power(int x, int y){
		var int flag;
		var int result;
		let flag = y;
		let result = 1;
		if(y=0){
			return 1;
		}
		while ( flag>0 ){
			let result = Math.multiply(result,x);
			let flag=flag-1;
		}
		return result; 
	}	
	
    /** Returns the integer part of the square root of x. */
    function int sqrt(int x) {
		var int y,j,flag,powerJ;
		var int n,halfN;
		let y=0;
		let n = Math.logTwo(x);
		let halfN = Math.divide(n,2);
		let j=halfN;
		if (x<0){
			return Sys.error(3);
		}
		while (j>-1){
			let powerJ = Math.power(2,j);
			let flag = y+powerJ;
			let flag = Math.multiply(flag,flag);
			if (((flag < x) | (flag = x)) & (flag > 0)){
				let y = y + powerJ;
			}
			let j=j-1;
		}
		return y;
    }

    /** Returns the greater number. */
    function int max(int a, int b) {
		if (a>b){
			return a;
		}
		else{
			return b;
		}
    }

    /** Returns the smaller number. */
    function int min(int a, int b) {
		if (a<b){
			return a ;
		}
		else{
			return b;
		}
    }
}



Array

Array模块的实现很easy,仅仅须要注意,它调用了Memory模块中的相关函数,以实现动态分配内存。

// 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/Array.jack

/**
 * Represents an array. Can be used to hold any type of object.
 */
class Array {

    /** Constructs a new Array of the given size. */
    function Array new(int size) {
	var Array a;
	let a=Memory.alloc(size);
	return a;
    }

    /** De-allocates the array and frees its space. */
    method void dispose() {
	do Memory.deAlloc(this);
	return;
    }
}




String

语言规范:field变量的使用

String的本质是一个数组。在每个String类中,都必须有四个全局參数。一个是数组a,代表了String的起始地址,一个是当前字符串长度stringLength和最大字符串长度allocLength(注意,这两个长度是不同的!

),另外还有推断String是否为负数字符串的negFlag。

 

负数的处理:

1。String.new(0) 參数小于0时最好报错,參数等于0时,默认给它赋1的空间。

2。数字与字符串相转换时,若数字为负数,须要加符号。这里用negFlag来判别。

 此外。本书的字符串编码与ASCII有细微差异,比如,书中将backspace定义为129,换行定义为128。读者仅仅需了解就可以。

// 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/String.jack

/**
 * Represents a String object. Implements the String type.
 */
class String {
    field Array a;
    field int stringLength; 
    field boolean negFlag;
    field int allocLength;

    /** Constructs a new empty String with a maximum length of maxLength. */
    constructor String new(int maxLength) {
        if (maxLength<1){
            let maxLength = 1;
        }
        let allocLength = maxLength;
        let negFlag = false;
        let a = Array.new(maxLength);
        let stringLength = 0;
        return this;
    }

    /** De-allocates the string and frees its space. */
    method void dispose() {
        do a.dispose();
        return;
    }

    /** Returns the current length of this String. */
    method int length() {
        return stringLength;
    }

    /** Returns the character at location j. */
    method char charAt(int j) {
        var char c;
        let c=a[j];
        return c;
    }

    /** Sets the j"th character of this string to be c. */
    method void setCharAt(int j, char c) {
        let a[j]=c;
        return;
    }

    /** Appends the character c to the end of this String.
     *  Returns this string as the return value. */
    method String appendChar(char c) {
        var int length;
        if(stringLength=allocLength){
            do Sys.error(17);
        }
        let length = stringLength;
        let a[length] = c;
        let stringLength=stringLength+1;
        return this;
    }

    /** Erases the last character from this String. */
    method void eraseLastChar() {
        var int length;
        let length = stringLength;
        let stringLength=stringLength-1;
        return;
    }

    /** Returns the integer value of this String until the first non
     *  numeric character. */
    method int intValue() {
        var int length,i,result;
        var int temp;
        var boolean flag;
        let flag=false;
        let i=0;
        let length = stringLength;
        let result = 0;
        if (a[0]=45){
            let flag = true;
            let i=i+1;
        }
        while (i < length){
            if ((a[i]>47)&(a[i]<58)){            
                let temp = a[i]-48;
                let result = Math.multiply(result,10) + temp;
                let i=i+1;
            }
            else{
                if (flag){
                    let result = -result;
                }
                return result;
            }
        }
        if (flag){
            let result = -result;
        }
        return result;
    }

    /** Sets this String to hold a representation of the given number. */
    method void setInt(int number) {
        var int lastDigit;
        var int divNumber,tenNumber;
        var int c;
        let stringLength = 0;
        if (number < 0){
            let negFlag = true;
            let number = Math.abs(number);
        }
        let divNumber = Math.divide(number,10);
        let tenNumber = Math.multiply(divNumber,10);
        let lastDigit = number - tenNumber;
        let c = lastDigit+48;
        if (number<10){
            if (negFlag){
                do appendChar(45);
                let negFlag = false;
            }
            do appendChar(c);
        }
        else{
            do setInt(divNumber);
            do appendChar(c);
        }
        return;
    }

    /** Returns the new line character. */
    function char newLine() {
        return 128;
    }

    /** Returns the backspace character. */
    function char backSpace() {
        return 129;
    }

    /** Returns the double quote (") character. */
    function char doubleQuote() {
        return 34;
    }
}




Memory

Memory模块的实现比較难入手。由于当中涉及到对于堆栈的操作。

并且,内置的OS算法和书中给出的算法并不同样,我更倾向于内置的算法,它比較易于实现。

(图中第一列代表堆栈位置,第二列代表存储数据)

图一:


图二:


图一是堆栈的初始状态,起始位置为2048,当中存储的14334代表从2050開始到16383,一共同拥有14334个可用空间。2049的位置则表示下一个指针指向的位置。

图二表示的是堆栈经过一次alloc后的状态。在这儿,我们做的事alloc(2)。也就是取出了两个栈空间使用。从图中能够看到,分配完两个空间后。此时的2048位置已经变为了0。假设下一次指针游走到这个位置,它就会明确该空间已经被占领,须要往下移一个位置到2049,寻找下一个节点(2052),到了下一个节点。发现空间为14330。假设够用的话。就依照上述规律在当中分配空间。

能够看到。事实上每一次分配的空间并不等于size,而是size+2。

 

PS:我给出的代码没有完毕整合碎片的步骤,请读者自行思考。


// 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/Memory.jack

/**
 * Memory operations library.
 */ 
class Memory {

    static Array freelist; 

    /** Initializes memory parameters. */
    function void init() {
        let freelist = 0;
        let freelist[2048]=14334;
        let freelist[2049]=2050;
        return;
    }

    /** Returns the value of the main memory at the given address. */
    function int peek(int address) {
        return freelist[address];
    }

    /** Sets the value of the main memory at this address
     *  to the given value. */
    function void poke(int address, int value) {
        let freelist[address] = value;
        return;
    }

    /** finds and allocates from the heap a memory block of the 
     *  specified size and returns a reference to its base address. */
    function int alloc(int size) {
        var int listRoom,listTag,tempAdd,returnVal;
        var int minSize;
        let minSize=size+3;
        let listTag=2048;
        let listRoom=Memory.peek(listTag);
        while(minSize>listRoom){
            let listTag=listTag+1;
            let listTag=Memory.peek(listTag);
            let listRoom=Memory.peek(listTag);
            if(listTag=0) {
                do Sys.error(7);
            }
        }
        let returnVal=listTag+2;
        do Memory.poke(listTag,0);
        let listTag=listTag+1;
        let tempAdd=Memory.peek(listTag)+size;
        do Memory.poke(listTag,tempAdd);
        do Memory.poke(tempAdd,listRoom-size-2);
        let listTag=tempAdd+1;
        do Memory.poke(listTag,listTag+1);          
        return returnVal;
    }

    /** De-allocates the given object and frees its space. */
    function void deAlloc(int object) {
        var int length;
        let length = Memory.peek(object+1)-object-2;
        do Memory.poke(object,length);
        return;
    }    
}


 

Screen:

像素位的分配:

首先,我们须要搞清楚。这256*512的像素阵列是怎样存入16384~24575的RAM中的。

显存共8192个字,每一个字有16位,8192*16=256*512,所以每一个像素仅仅占一个比特位。

全部像素从左上角(0,0)位置開始,依照自左向右。自上而下的顺序排列,须要注意的是,第一个像素点是存在第一个字的第一位。而非第十六位。

 

此外要注意的是,依照书上的算法,逐个画像素也能完毕画线功能,可是这在实际应用中的效率之低是无法忍受的。假设你这个Screen.vm应用到PongGame中。你会发现其执行速度很之慢,所以,对于画线,后期最好将算法改进为按字填色。

// 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

/**
 * Graphic screen library.
 */
class Screen {
    static boolean color;

    /** Initializes the Screen. */
    function void init() {
        let color = true;
        return;
    }

    /** Erases the whole screen. */
    function void clearScreen() {
        var int address;
        let address=16384;
        while(address<24576){
            do Memory.poke(address,0);
            let address=address+1;
        }
        return;
    }

    /** Sets the color to be used in further draw commands
     *  where white = false, black = true. */
    function void setColor(boolean b) {
        let color=b;
        return;
    }

    /** Draws the (x, y) pixel. */
    function void drawPixel(int x, int y) {
        var int i,divNum,address,remain,temp;
        if((x>511)|(y>255)){
            do Sys.error(11);
        }
        let i=1;
        let divNum=Math.divide(x,16);
        let address=16384+Math.multiply(y,32)+divNum;
        let remain=x-Math.multiply(divNum,16);
        let temp=Memory.peek(address);
        while(remain>0){
            let i=i+i;
            let remain=remain-1;
        }
        //if color = false, negate the i to draw white pixel
        if(color){
            let temp=(temp|i);          
        }
        else{
            let i = ~i;
            let temp = (temp&i);
        }
        do Memory.poke(address,temp);  
        return;
    }

    /** Draws a line from (x1, y1) to (x2, y2). */
    function void drawLine(int x1, int y1, int x2, int y2) {
        var int a,b,dx,dy,compDx,compDy,adyMinusbdx;
        let dx=x2-x1;
        let dy=y2-y1;
        let compDx=Math.abs(dx)+1;
        let compDy=Math.abs(dy)+1;
        let a=0;
        let b=0;
        if (dy=0){
            if(dx>0){
                while(a<compDx){
                    do Screen.drawPixel(x1+a,y1);
                    let a=a+1;               
                }
                return;                
            }
            else{
                while(a<compDx){
                    do Screen.drawPixel(x1-a,y1);
                    let a=a+1;               
                }
                return;  
            }
        }
        if (dx=0){
            if(dy>0){
                while(a<compDy){
                    do Screen.drawPixel(x1,y1+a);
                    let a=a+1;               
                }
                return;                
            }
            else{
                while(a<compDy){
                    do Screen.drawPixel(x1,y1-a);
                    let a=a+1;               
                }
                return;  
            }
        }

        if((dx>0)&(dy>0)){
            let adyMinusbdx=0;
            while((a<compDx)&(b<compDy)){
                do Screen.drawPixel(x1+a,y1+b);
                if(adyMinusbdx<0){
                    let adyMinusbdx=adyMinusbdx+dy;
                    let a=a+1;
                }
                else{
                    let adyMinusbdx=adyMinusbdx-dx;
                    let b=b+1;
                }
            }
            return;
        }

        if((dx<1)&(dy<1)){
            let adyMinusbdx=0;
            while((a<compDx)&(b<compDy)){
                do Screen.drawPixel(x2+a,y2+b);
                if(adyMinusbdx<0){
                    let adyMinusbdx=adyMinusbdx-dy;
                    let a=a+1;
                }
                else{
                    let adyMinusbdx=adyMinusbdx+dx;
                    let b=b+1;
                }
            }
            return;
        }

        if((dx>0)&(dy<1)){
            let adyMinusbdx=0;
            while((a<compDx)&(b<compDy)){
                do Screen.drawPixel(x1+a,y1-b);
                if(adyMinusbdx<0){
                    let adyMinusbdx=adyMinusbdx-dy;
                    let a=a+1;
                }
                else{
                    let adyMinusbdx=adyMinusbdx-dx;
                    let b=b+1;
                }
            }
            return;
        }

        if((dy>0)&(dx<1)){
            let adyMinusbdx=0;
            while((a<compDx)&(b<compDy)){
                do Screen.drawPixel(x2+a,y2-b);
                if(adyMinusbdx<0){
                    let adyMinusbdx=adyMinusbdx+dy;
                    let a=a+1;
                }
                else{
                    let adyMinusbdx=adyMinusbdx+dx;
                    let b=b+1;
                }
            }
            return;
        }
        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) {
        var int y;
        let y=y1;
        while(y<y2){
            do Screen.drawLine(x1,y,x2,y);
            let y=y+1;
        }
        do Screen.drawLine(x1,y,x2,y);
        return;
    }

    /** Draws a filled circle of radius r around (cx, cy). */
    function void drawCircle(int cx, int cy, int r) {
        var int y,dy,dyPower,rPower,halfDistance,sqrNum,xLeft,xRight;
        if((cx>511)|(cy>255)|(r>181)){
            do Sys.error(12);
        }
        let dy=-r;
        let rPower=Math.multiply(r,r);
        while(dy<r){
            let dyPower=Math.multiply(dy,dy);
            let halfDistance=rPower-dyPower;
            let sqrNum=Math.sqrt(halfDistance);
            let xLeft=cx-sqrNum;
            let xRight=cx+sqrNum;
            let y=cy+dy;
            do Screen.drawLine(xLeft,y,xRight,y);
            let dy=dy+1;
        }
        do Screen.drawPixel(cx,cy+r);
        return;
    }
}




Output:

编码与字符的转换:

尽管我们早就对字符显示在屏幕上这样的事情习以为常,可是它涉及的各项操作还真是挺复杂的。

首先要注意的是,因为全屏被分为23*64个字符区域,而每一行仅仅有16个字段。因此。每个字其实都包括了两个字符的当中的一行(也就是说上下区域连续11个字节才表示两个完整的字符),所以说。奇数列和偶数列的字符操作是不同的:偶数列(从第0列開始)仅仅须要将对应的表示填充位置的数字读入对应的字段,而奇数列则须要将该数字向右移8位。再加上原先的偶数列中的数据才干读入对应字段。

第二点。光标的设置并没有规定。可是设置为32(也就是空字符)操作起来相对方便一些。这种话,光标移到哪里,就会直接将原区域的数据清零。方便接下来的操作。

// 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/Output.jack

/**
 * Handles writing characters to the screen.
 * The text screen (256 columns and 512 roes) is divided into 23 text rows (0..22), 
 * each containing 64 text columns (0..63).
 * Each row is 11 pixels high (including 1 space pixel), and 8 pixels wide
 * (including 2 space pixels).
 */
class Output {

    // Character map for printing on the left of a screen word
    static Array charMaps;
    static int cursorRow,cursorCol; //record the actual pixel line in 256*512
    static int charRow,charCol; //record the char line in 23*64

    /** Initializes the screen and locates the cursor at the screen's top-left. */
    function void init() {
        let cursorRow=0;
        let cursorCol=0;
        let charRow=0;
        let charCol=0;
        do Output.initMap();
        return;
    }

    // Initalizes the character map array
    function void initMap() {
        var int i;
    
        let charMaps = Array.new(127);
        
        // black square (used for non printable characters)
        do Output.create(0,63,63,63,63,63,63,63,63,63,0,0);

        // Assigns the bitmap for each character in the charachter set.
        do Output.create(32,0,0,0,0,0,0,0,0,0,0,0);          //
        do Output.create(33,12,30,30,30,12,12,0,12,12,0,0);  // !
        do Output.create(34,54,54,20,0,0,0,0,0,0,0,0);       // "
        do Output.create(35,0,18,18,63,18,18,63,18,18,0,0);  // #
        do Output.create(36,12,30,51,3,30,48,51,30,12,12,0); // $
        do Output.create(37,0,0,35,51,24,12,6,51,49,0,0);    // %
        do Output.create(38,12,30,30,12,54,27,27,27,54,0,0); // &
        do Output.create(39,12,12,6,0,0,0,0,0,0,0,0);        // '
        do Output.create(40,24,12,6,6,6,6,6,12,24,0,0);      // (
        do Output.create(41,6,12,24,24,24,24,24,12,6,0,0);   // )
        do Output.create(42,0,0,0,51,30,63,30,51,0,0,0);     // *
        do Output.create(43,0,0,0,12,12,63,12,12,0,0,0);     // +
        do Output.create(44,0,0,0,0,0,0,0,12,12,6,0);        // ,
        do Output.create(45,0,0,0,0,0,63,0,0,0,0,0);         // -
        do Output.create(46,0,0,0,0,0,0,0,12,12,0,0);        // .    
        do Output.create(47,0,0,32,48,24,12,6,3,1,0,0);      // /
        
        do Output.create(48,12,30,51,51,51,51,51,30,12,0,0); // 0
        do Output.create(49,12,14,15,12,12,12,12,12,63,0,0); // 1
        do Output.create(50,30,51,48,24,12,6,3,51,63,0,0);   // 2
        do Output.create(51,30,51,48,48,28,48,48,51,30,0,0); // 3
        do Output.create(52,16,24,28,26,25,63,24,24,60,0,0); // 4
        do Output.create(53,63,3,3,31,48,48,48,51,30,0,0);   // 5
        do Output.create(54,28,6,3,3,31,51,51,51,30,0,0);    // 6
        do Output.create(55,63,49,48,48,24,12,12,12,12,0,0); // 7
        do Output.create(56,30,51,51,51,30,51,51,51,30,0,0); // 8
        do Output.create(57,30,51,51,51,62,48,48,24,14,0,0); // 9
        
        do Output.create(58,0,0,12,12,0,0,12,12,0,0,0);      // :
        do Output.create(59,0,0,12,12,0,0,12,12,6,0,0);      // ;
        do Output.create(60,0,0,24,12,6,3,6,12,24,0,0);      // <
        do Output.create(61,0,0,0,63,0,0,63,0,0,0,0);        // =
        do Output.create(62,0,0,3,6,12,24,12,6,3,0,0);       // >
        do Output.create(64,30,51,51,59,59,59,27,3,30,0,0);  // @
        do Output.create(63,30,51,51,24,12,12,0,12,12,0,0);  // ?

do Output.create(65,12,30,51,51,63,51,51,51,51,0,0); // A ** TO BE FILLED ** do Output.create(66,31,51,51,51,31,51,51,51,31,0,0); // B do Output.create(67,28,54,35,3,3,3,35,54,28,0,0); // C do Output.create(68,15,27,51,51,51,51,51,27,15,0,0); // D do Output.create(69,63,51,35,11,15,11,35,51,63,0,0); // E do Output.create(70,63,51,35,11,15,11,3,3,3,0,0); // F do Output.create(71,28,54,35,3,59,51,51,54,44,0,0); // G do Output.create(72,51,51,51,51,63,51,51,51,51,0,0); // H do Output.create(73,30,12,12,12,12,12,12,12,30,0,0); // I do Output.create(74,60,24,24,24,24,24,27,27,14,0,0); // J do Output.create(75,51,51,51,27,15,27,51,51,51,0,0); // K do Output.create(76,3,3,3,3,3,3,35,51,63,0,0); // L do Output.create(77,33,51,63,63,51,51,51,51,51,0,0); // M do Output.create(78,51,51,55,55,63,59,59,51,51,0,0); // N do Output.create(79,30,51,51,51,51,51,51,51,30,0,0); // O do Output.create(80,31,51,51,51,31,3,3,3,3,0,0); // P do Output.create(81,30,51,51,51,51,51,63,59,30,48,0);// Q do Output.create(82,31,51,51,51,31,27,51,51,51,0,0); // R do Output.create(83,30,51,51,6,28,48,51,51,30,0,0); // S do Output.create(84,63,63,45,12,12,12,12,12,30,0,0); // T do Output.create(85,51,51,51,51,51,51,51,51,30,0,0); // U do Output.create(86,51,51,51,51,51,30,30,12,12,0,0); // V do Output.create(87,51,51,51,51,51,63,63,63,18,0,0); // W do Output.create(88,51,51,30,30,12,30,30,51,51,0,0); // X do Output.create(89,51,51,51,51,30,12,12,12,30,0,0); // Y do Output.create(90,63,51,49,24,12,6,35,51,63,0,0); // Z do Output.create(91,30,6,6,6,6,6,6,6,30,0,0); // [ do Output.create(92,0,0,1,3,6,12,24,48,32,0,0); // \ do Output.create(93,30,24,24,24,24,24,24,24,30,0,0); // ] do Output.create(94,8,28,54,0,0,0,0,0,0,0,0); // ^ do Output.create(95,0,0,0,0,0,0,0,0,0,63,0); // _ do Output.create(96,6,12,24,0,0,0,0,0,0,0,0); // ` do Output.create(97,0,0,0,14,24,30,27,27,54,0,0); // a do Output.create(98,3,3,3,15,27,51,51,51,30,0,0); // b do Output.create(99,0,0,0,30,51,3,3,51,30,0,0); // c do Output.create(100,48,48,48,60,54,51,51,51,30,0,0); // d do Output.create(101,0,0,0,30,51,63,3,51,30,0,0); // e do Output.create(102,28,54,38,6,15,6,6,6,15,0,0); // f do Output.create(103,0,0,30,51,51,51,62,48,51,30,0); // g do Output.create(104,3,3,3,27,55,51,51,51,51,0,0); // h do Output.create(105,12,12,0,14,12,12,12,12,30,0,0); // i do Output.create(106,48,48,0,56,48,48,48,48,51,30,0); // j do Output.create(107,3,3,3,51,27,15,15,27,51,0,0); // k do Output.create(108,14,12,12,12,12,12,12,12,30,0,0); // l do Output.create(109,0,0,0,29,63,43,43,43,43,0,0); // m do Output.create(110,0,0,0,29,51,51,51,51,51,0,0); // n do Output.create(111,0,0,0,30,51,51,51,51,30,0,0); // o do Output.create(112,0,0,0,30,51,51,51,31,3,3,0); // p do Output.create(113,0,0,0,30,51,51,51,62,48,48,0); // q do Output.create(114,0,0,0,29,55,51,3,3,7,0,0); // r do Output.create(115,0,0,0,30,51,6,24,51,30,0,0); // s do Output.create(116,4,6,6,15,6,6,6,54,28,0,0); // t do Output.create(117,0,0,0,27,27,27,27,27,54,0,0); // u do Output.create(118,0,0,0,51,51,51,51,30,12,0,0); // v do Output.create(119,0,0,0,51,51,51,63,63,18,0,0); // w do Output.create(120,0,0,0,51,30,12,12,30,51,0,0); // x do Output.create(121,0,0,0,51,51,51,62,48,24,15,0); // y do Output.create(122,0,0,0,63,27,12,6,51,63,0,0); // z do Output.create(123,56,12,12,12,7,12,12,12,56,0,0); // { do Output.create(124,12,12,12,12,12,12,12,12,12,0,0); // | do Output.create(125,7,12,12,12,56,12,12,12,7,0,0); // } do Output.create(126,38,45,25,0,0,0,0,0,0,0,0); // ~ return; } // Creates a character map array of the given char index with the given values. function void create(int index, int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k) { var Array map; let map = Array.new(11); let charMaps[index] = map; let map[0] = a; let map[1] = b; let map[2] = c; let map[3] = d; let map[4] = e; let map[5] = f; let map[6] = g; let map[7] = h; let map[8] = i; let map[9] = j; let map[10] = k; return; } // Returns the character map (array of size 11) for the given character // If an invalid character is given, returns the character map of a black square. function Array getMap(char c) { if ((c < 32) | (c > 126)) { let c = 0; } return charMaps[c]; } /** Moves the cursor to the j抰h column of the i抰h row, * and erases the character that was there. */ function void moveCursor(int i, int j) { var boolean oddOrEven; var int column,row,flag,tempVal; var int divNum,address; var int tempChar,oriChar,answer; var Array zeroMap; let flag=0; if(i=23){ let cursorRow=0; let cursorCol=0; let charRow=0; let charCol=0; } else{ let cursorRow=Math.multiply(i,11); let cursorCol=Math.multiply(j,8); let charRow=i; let charCol=j; } let row=cursorRow; let column=cursorCol; //define zeroMap[] let zeroMap = Output.getMap(32); //find address let divNum=Math.divide(column,16); let address=16384+Math.multiply(row,32)+divNum; //print char 0 let tempVal=Math.multiply(Math.divide(charCol,2),2); if(tempVal=charCol){ let oddOrEven=true; } else{ let oddOrEven=false; } if(~oddOrEven){ while(flag<11){ let tempChar=Math.multiply(zeroMap[flag],256); let oriChar=Memory.peek(address); let oriChar=(oriChar&255); let answer=tempChar+oriChar; do Memory.poke(address,answer); let address=address+32; let flag=flag+1; } } else{ while(flag<11){ do Memory.poke(address,zeroMap[flag]); let address=address+32; let flag=flag+1; } } return; } /** Prints c at the cursor location and advances the cursor one * column forward. */ function void printChar(char c) { var int row,column; var int i,divNum,address,oriChar,tempChar,answer,tempVal; var boolean oddOrEven; var Array characterMap; let i=0; let characterMap=Output.getMap(c); let row=cursorRow; let column=cursorCol; //find address let divNum=Math.divide(column,16); let address=16384+Math.multiply(row,32)+divNum; let tempVal=Math.multiply(Math.divide(charCol,2),2); if(tempVal=charCol){ let oddOrEven=true; } else{ let oddOrEven=false; } if(~oddOrEven){ while(i<11){ let tempChar=Math.multiply(characterMap[i],256); let oriChar=Memory.peek(address); let answer=tempChar+oriChar; do Memory.poke(address,answer); let address=address+32; let i=i+1; } } else{ while(i<11){ do Memory.poke(address,characterMap[i]); let address=address+32; let i=i+1; } } if(charCol=63){ do Output.println(); return; } else{ let charCol=charCol+1; do Output.moveCursor(charRow,charCol); return; } } /** Prints s starting at the cursor location, and advances the * cursor appropriately. */ function void printString(String s) { var int strLength; var char temp; var int i; let i=0; let strLength=s.length(); while(i<strLength){ let temp = s.charAt(i); do Output.printChar(temp); let i=i+1; } return; } /** Prints i starting at the cursor location, and advances the * cursor appropriately. */ function void printInt(int i) { var String str; let str = String.new(6); do str.setInt(i); do Output.printString(str); return; } /** Advances the cursor to the beginning of the next line. */ function void println() { let charRow=charRow+1; let charCol=0; do Output.moveCursor(charRow,charCol); return; } /** Moves the cursor one column back. */ function void backSpace() { var boolean oddOrEven; var int column,row,flag,tempVal; var int divNum,address; var int tempChar,oriChar,answer; var Array zeroMap; if(charCol=0){ if(charRow>0){ let charRow=charRow-1; let charCol=63; } } else{ let charCol=charCol-1; } do Output.moveCursor(charRow,charCol); return; } }






Keyboard:

Keyboard的实现相对简单。依照书上的伪代码操作就可以。

// 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/Keyboard.jack

/**
 * A library for handling user input from the keyboard.
 */
class Keyboard {

    /** Initializes the keyboard. */
    function void init() {
        return;
    } 

    /**
     * Returns the ASCII code (as char) of the currently pressed key,
     * or 0 if no key is currently pressed.
     * Recognizes all ASCII characters, as well as the following extension
     * of action keys:
     * New line = 128 = String.newline()
     * Backspace = 129 = String.backspace()
     * Left Arrow = 130
     * Up Arrow = 131
     * Right Arrow = 132
     * Down Arrow = 133
     * Home = 134
     * End = 135
     * Page Up = 136
     * Page Down = 137
     * Insert = 138
     * Delete = 139
     * ESC = 140
     * F1 - F12 = 141 - 152
     */
    function char keyPressed() {  
        return Memory.peek(24576);
    }

    /**								
     * Reads the next character from the keyboard.
     * waits until a key is pressed and then released, then echoes
     * the key to the screen, and returns the value of the pressed key.
     */
    function char readChar() {
        var char c;
        while(~(Keyboard.keyPressed())){}
        let c=Keyboard.keyPressed();
        while(c=Keyboard.keyPressed()){}
        do Output.printChar(c);
        return c;
    }

    /**								
     * Prints the message on the screen, reads the next line
     * (until a newline character) from the keyboard, and returns its value.
     */
    function String readLine(String message) {
        var String s;
        var char c;
        do Output.printString(message);
        let s=String.new(100);
        while(true){
            let c=Keyboard.readChar();
            if (c=128){
                do Output.printChar(128);
                return s;
            }
            if (c=129){
                do s.eraseLastChar();
                do Output.backSpace();
            }
            else{
                let s=s.appendChar(c);
            }
        }
        return s;
    }   

    /**								
     * Prints the message on the screen, reads the next line
     * (until a newline character) from the keyboard, and returns its
     * integer value (until the first non numeric character).
     */
    function int readInt(String message) {
        var String s;
        let s=Keyboard.readLine(message);
        return s.intValue();
    }
}


Sys:

Sys的实现也很easy,须要注意的是wait函数,在实现它时,我们採取1ms=50times的策略模拟就可以。

// 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/Sys.jack

/**
 * A library of basic system services.
 */
class Sys {

    /** Performs all the initializations required by the OS. */
    function void init() {
        do Memory.init();
        do Math.init();
        do Screen.init();
        do Output.init();
        do Keyboard.init();
        do Main.main();
        do Sys.halt();
        return;
    }

    /** Halts execution. */
    function void halt() {
        while(true){}
        return;
    }

    /** Waits approximately duration milliseconds and then returns. */
    function void wait(int duration) {
        var int temp;
        if(duration<0){
            do Sys.error(1);
        }
        while(duration>0){
            let temp=50;
            while(temp>0){
                let temp=temp-1;
            }
            let duration=duration-1;
        }
        return;
    }

    /** Prints the given error code in the form "ERR<errorCode>", and halts. */
    function void error(int errorCode) {
        var String s;
        let s=String.new(3);
        let s="ERR";
        do Output.printString(s);
        do Output.printInt(errorCode);
        do Sys.halt();
        return;
    }
}






本文转自mfrbuaa博客园博客,原文链接:http://www.cnblogs.com/mfrbuaa/p/5055052.html,如需转载请自行联系原作者


相关文章
|
7月前
|
关系型数据库 虚拟化 UED
Omnissa Horizon Windows OS Optimization Tool 2503 - Windows 系统映像优化工具
Omnissa Horizon Windows OS Optimization Tool 2503 - Windows 系统映像优化工具
303 7
Omnissa Horizon Windows OS Optimization Tool 2503 - Windows 系统映像优化工具
|
3月前
|
JSON iOS开发 数据格式
最新研发flutter3.32+window_manager客户端OS管理系统
原创Flutter3.32+Dart3.8+Getx+Window_Manager实战桌面客户端os系统解决方案。支持macOS和windows两种主题风格、自定义桌面栅格布局。
327 50
|
运维 安全 Devops
Cisco NX-OS ACI 16.1(4h)F 发布 - 适用于 ACI 模式下的 Cisco Nexus 9000 系列交换机系统软件
Cisco NX-OS ACI 16.1(4h)F 发布 - 适用于 ACI 模式下的 Cisco Nexus 9000 系列交换机系统软件
73 0
|
9月前
|
存储 Linux API
【Linux进程概念】—— 操作系统中的“生命体”,计算机里的“多线程”
在计算机系统的底层架构中,操作系统肩负着资源管理与任务调度的重任。当我们启动各类应用程序时,其背后复杂的运作机制便悄然展开。程序,作为静态的指令集合,如何在系统中实现动态执行?本文带你一探究竟!
【Linux进程概念】—— 操作系统中的“生命体”,计算机里的“多线程”
|
4月前
|
Android开发 Windows
这是我设想的免重启操作系统的状态下更新通用计算机、嵌入式操作系统的软件设计思路
本方案提出了一种名为slfm的软件系统,旨在实现通用计算机及嵌入式系统在不重启状态下完成操作系统更新。其核心机制是通过构建独立于原系统的运行环境(slfm Recovery与The Tube),在高权限模式下进行系统文件更新与切换,确保更新过程中设备持续运行,适用于普通设备与不可中断服务的关键系统(如医疗、服务器等)。同时具备失败回滚、数据同步、权限隔离等功能,提升系统更新的安全性与可用性。
|
5月前
|
Cloud Native 安全 Linux
龙蜥操作系统:CentOS 谢幕之后,国产云原生系统的崛起之路
龙蜥操作系统(Anolis OS)是 CentOS 停止维护后,由阿里云等企业联合发起的开源项目。它以双内核架构和全栈优化为核心,提供无缝替代 CentOS 的方案,兼容主流生态并针对云计算场景深度优化。其技术亮点包括 RHCK 和 ANCK 双内核、性能优化、全栈安全及国密算法支持。龙蜥适用于云原生基础设施、企业级应用部署及开发环境,社区已吸引 200 多家单位参与。未来规划涵盖 AI 框架优化、RISC-V 架构适配及桌面环境构建,正重新定义云时代的操作系统边界。
1284 0
|
8月前
|
监控 关系型数据库 MySQL
zabbix7.0.9安装-以宝塔安装形式-非docker容器安装方法-系统采用AlmaLinux9系统-最佳匹配操作系统提供稳定运行环境-安装教程完整版本-优雅草卓伊凡
zabbix7.0.9安装-以宝塔安装形式-非docker容器安装方法-系统采用AlmaLinux9系统-最佳匹配操作系统提供稳定运行环境-安装教程完整版本-优雅草卓伊凡
591 30
|
9月前
|
安全 前端开发 开发工具
【01】鸿蒙实战应用开发-华为鸿蒙纯血操作系统Harmony OS NEXT-项目开发实战-优雅草卓伊凡拟开发一个一站式家政服务平台-前期筹备-暂定取名斑马家政软件系统-本项目前端开源-服务端采用优雅草蜻蜓Z系统-搭配ruoyi框架admin后台-全过程实战项目分享-从零开发到上线
【01】鸿蒙实战应用开发-华为鸿蒙纯血操作系统Harmony OS NEXT-项目开发实战-优雅草卓伊凡拟开发一个一站式家政服务平台-前期筹备-暂定取名斑马家政软件系统-本项目前端开源-服务端采用优雅草蜻蜓Z系统-搭配ruoyi框架admin后台-全过程实战项目分享-从零开发到上线
472 5
【01】鸿蒙实战应用开发-华为鸿蒙纯血操作系统Harmony OS NEXT-项目开发实战-优雅草卓伊凡拟开发一个一站式家政服务平台-前期筹备-暂定取名斑马家政软件系统-本项目前端开源-服务端采用优雅草蜻蜓Z系统-搭配ruoyi框架admin后台-全过程实战项目分享-从零开发到上线
|
8月前
|
弹性计算 运维 监控
操作系统控制台-健康守护我们的系统
阿里云操作系统控制平台作为新一代云端服务器中枢平台,通过创新交互模式重构主机管理体验。用户可通过API、SDK、CLI等方式进行系统管理,采用图形化控制替代传统命令行操作,集智能运维、集群协调、生态扩展于一体,显著提升企业级IT设施管理效能。通过此平台,用户可以轻松实现运维监控、智能助手、扩展插件管理及订阅服务等功能,大幅降低运维复杂度,提高管理效率。
217 11

推荐镜像

更多