从零开始搭建计算机

这个专题的主要内容是从最基础的与非门,一步步搭建出计算机的硬件结构,再到操作系统,最后实现一台可以运行任何想要程序的计算机。其中会涉及到编译器,虚拟机等等一系列知识。

不多说,开始了。

Step 1 Logic Gate

以下共实现了 16 个逻辑门。

Not

// 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/1/Not.hdl
/**
* Not gate:
* if (in) out = 0, else out = 1
*/
CHIP Not {
IN in;
OUT out;

PARTS:
//// Replace this comment with your code.
Nand(a = in, b = in, out = out);
}

And

// 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/1/And.hdl
/**
* And gate:
* if (a and b) out = 1, else out = 0
*/
CHIP And {
IN a, b;
OUT out;

PARTS:
//// Replace this comment with your code.
Nand(a = a, b = b, out = temp);
Not(in = temp, out = out);
}

Or

// 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/1/Or.hdl
/**
* Or gate:
* if (a or b) out = 1, else out = 0
*/
CHIP Or {
IN a, b;
OUT out;

PARTS:
Not(in = a, out = nota);
Not(in = b, out = notb);
Nand(a = nota, b = notb, out = out);
}

Xor

// 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/1/Xor.hdl
/**
* Exclusive-or gate:
* if ((a and Not(b)) or (Not(a) and b)) out = 1, else out = 0
*/
CHIP Xor {
IN a, b;
OUT out;

PARTS:
//// Replace this comment with your code.
Not(in = a, out = nota);
Not(in = b, out = notb);
And(a = nota, b = b, out = temp1);
And(a = a, b = notb, out = temp2);
Or(a = temp1, b = temp2, out = out);
}

Mux

// 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/1/Mux.hdl
/**
* Multiplexor:
* if (sel = 0) out = a, else out = b
*/
CHIP Mux {
IN a, b, sel;
OUT out;

PARTS:
//// Replace this comment with your code.
Not(in = sel, out = notSel);
And(a = a, b = notSel, out = temp1);
And(a = b, b = sel, out = temp2);
Or(a = temp1, b = temp2, out = out);
}

DMux

// 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/1/DMux.hdl
/**
* Demultiplexor:
* [a, b] = [in, 0] if sel = 0
* [0, in] if sel = 1
*/
CHIP DMux {
IN in, sel;
OUT a, b;

PARTS:
//// Replace this comment with your code.
Not(in = sel, out = notSel);
And(a = in, b = notSel, out = a);
And(a = in, b = sel, out = b);
}

Not16

// 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/01/Not16.hdl
/**
* 16-bit Not gate:
* for i = 0, ..., 15:
* out[i] = Not(a[i])
*/
CHIP Not16 {
IN in[16];
OUT out[16];

PARTS:
//// Replace this comment with your code.
Not(in = in[0], out = out[0]);
Not(in = in[1], out = out[1]);
Not(in = in[2], out = out[2]);
Not(in = in[3], out = out[3]);
Not(in = in[4], out = out[4]);
Not(in = in[5], out = out[5]);
Not(in = in[6], out = out[6]);
Not(in = in[7], out = out[7]);
Not(in = in[8], out = out[8]);
Not(in = in[9], out = out[9]);
Not(in = in[10], out = out[10]);
Not(in = in[11], out = out[11]);
Not(in = in[12], out = out[12]);
Not(in = in[13], out = out[13]);
Not(in = in[14], out = out[14]);
Not(in = in[15], out = out[15]);
}

And16

// 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/1/And16.hdl
/**
* 16-bit And gate:
* for i = 0, ..., 15:
* out[i] = a[i] And b[i]
*/
CHIP And16 {
IN a[16], b[16];
OUT out[16];

PARTS:
//// Replace this comment with your code.
And(a = a[0], b = b[0], out = out[0]);
And(a = a[1], b = b[1], out = out[1]);
And(a = a[2], b = b[2], out = out[2]);
And(a = a[3], b = b[3], out = out[3]);
And(a = a[4], b = b[4], out = out[4]);
And(a = a[5], b = b[5], out = out[5]);
And(a = a[6], b = b[6], out = out[6]);
And(a = a[7], b = b[7], out = out[7]);
And(a = a[8], b = b[8], out = out[8]);
And(a = a[9], b = b[9], out = out[9]);
And(a = a[10], b = b[10], out = out[10]);
And(a = a[11], b = b[11], out = out[11]);
And(a = a[12], b = b[12], out = out[12]);
And(a = a[13], b = b[13], out = out[13]);
And(a = a[14], b = b[14], out = out[14]);
And(a = a[15], b = b[15], out = out[15]);
}

Or16

// 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/1/Or16.hdl
/**
* 16-bit Or gate:
* for i = 0, ..., 15:
* out[i] = a[i] Or b[i]
*/
CHIP Or16 {
IN a[16], b[16];
OUT out[16];

PARTS:
//// Replace this comment with your code.
Or(a = a[0], b = b[0], out = out[0]);
Or(a = a[1], b = b[1], out = out[1]);
Or(a = a[2], b = b[2], out = out[2]);
Or(a = a[3], b = b[3], out = out[3]);
Or(a = a[4], b = b[4], out = out[4]);
Or(a = a[5], b = b[5], out = out[5]);
Or(a = a[6], b = b[6], out = out[6]);
Or(a = a[7], b = b[7], out = out[7]);
Or(a = a[8], b = b[8], out = out[8]);
Or(a = a[9], b = b[9], out = out[9]);
Or(a = a[10], b = b[10], out = out[10]);
Or(a = a[11], b = b[11], out = out[11]);
Or(a = a[12], b = b[12], out = out[12]);
Or(a = a[13], b = b[13], out = out[13]);
Or(a = a[14], b = b[14], out = out[14]);
Or(a = a[15], b = b[15], out = out[15]);
}

Mux16

// 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/1/Mux16.hdl
/**
* 16-bit multiplexor:
* for i = 0, ..., 15:
* if (sel = 0) out[i] = a[i], else out[i] = b[i]
*/
CHIP Mux16 {
IN a[16], b[16], sel;
OUT out[16];

PARTS:
//// Replace this comment with your code.
Mux(a = a[0], b = b[0], sel = sel, out = out[0]);
Mux(a = a[1], b = b[1], sel = sel, out = out[1]);
Mux(a = a[2], b = b[2], sel = sel, out = out[2]);
Mux(a = a[3], b = b[3], sel = sel, out = out[3]);
Mux(a = a[4], b = b[4], sel = sel, out = out[4]);
Mux(a = a[5], b = b[5], sel = sel, out = out[5]);
Mux(a = a[6], b = b[6], sel = sel, out = out[6]);
Mux(a = a[7], b = b[7], sel = sel, out = out[7]);
Mux(a = a[8], b = b[8], sel = sel, out = out[8]);
Mux(a = a[9], b = b[9], sel = sel, out = out[9]);
Mux(a = a[10], b = b[10], sel = sel, out = out[10]);
Mux(a = a[11], b = b[11], sel = sel, out = out[11]);
Mux(a = a[12], b = b[12], sel = sel, out = out[12]);
Mux(a = a[13], b = b[13], sel = sel, out = out[13]);
Mux(a = a[14], b = b[14], sel = sel, out = out[14]);
Mux(a = a[15], b = b[15], sel = sel, out = out[15]);
}

Or8Way

// 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/1/Or8Way.hdl
/**
* 8-way Or gate:
* out = in[0] Or in[1] Or ... Or in[7]
*/
CHIP Or8Way {
IN in[8];
OUT out;

PARTS:
//// Replace this comment with your code.
Or(a = in[0], b = in[1], out = t01);
Or(a = t01, b = in[2], out = t02);
Or(a = t02, b = in[3], out = t03);
Or(a = t03, b = in[4], out = t04);
Or(a = t04, b = in[5], out = t05);
Or(a = t05, b = in[6], out = t06);
Or(a = t06, b = in[7], out = out);
}

Mux4Way16

// 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/1/Mux4Way16.hdl
/**
* 4-way 16-bit multiplexor:
* out = a if sel = 00
* b if sel = 01
* c if sel = 10
* d if sel = 11
*/
CHIP Mux4Way16 {
IN a[16], b[16], c[16], d[16], sel[2];
OUT out[16];

PARTS:
//// Replace this comment with your code.
Mux16(a = a, b = c, sel = sel[1], out = temp1);
Mux16(a = b, b = d, sel = sel[1], out = temp2);
Mux16(a = temp1, b = temp2, sel = sel[0], out = out);
}

Mux8Way16

// 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/1/Mux8Way16.hdl
/**
* 8-way 16-bit multiplexor:
* out = a if sel = 000
* b if sel = 001
* c if sel = 010
* d if sel = 011
* e if sel = 100
* f if sel = 101
* g if sel = 110
* h if sel = 111
*/
CHIP Mux8Way16 {
IN a[16], b[16], c[16], d[16],
e[16], f[16], g[16], h[16],
sel[3];
OUT out[16];

PARTS:
//// Replace this comment with your code.
Mux16(a = a, b = b, sel = sel[0], out = temp1);
Mux16(a = c, b = d, sel = sel[0], out = temp2);
Mux16(a = e, b = f, sel = sel[0], out = temp3);
Mux16(a = g, b = h, sel = sel[0], out = temp4);
Mux16(a = temp1, b = temp2, sel = sel[1], out = final0);
Mux16(a = temp3, b = temp4, sel = sel[1], out = final1);
Mux16(a = final0, b = final1, sel = sel[2], out = out);
}

DMux4Way16

// 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/1/DMux4Way.hdl
/**
* 4-way demultiplexor:
* [a, b, c, d] = [in, 0, 0, 0] if sel = 00
* [0, in, 0, 0] if sel = 01
* [0, 0, in, 0] if sel = 10
* [0, 0, 0, in] if sel = 11
*/
CHIP DMux4Way {
IN in, sel[2];
OUT a, b, c, d;

PARTS:
//// Replace this comment with your code.
DMux(in = in, sel = sel[1], a = temp1, b = temp2);
DMux(in = temp1, sel = sel[0], a = a, b = b);
DMux(in = temp2, sel = sel[0], a = c, b = d);
}

DMux8Way

// 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/1/DMux8Way.hdl
/**
* 8-way demultiplexor:
* [a, b, c, d, e, f, g, h] = [in, 0, 0, 0, 0, 0, 0, 0] if sel = 000
* [0, in, 0, 0, 0, 0, 0, 0] if sel = 001
* [0, 0, in, 0, 0, 0, 0, 0] if sel = 010
* [0, 0, 0, in, 0, 0, 0, 0] if sel = 011
* [0, 0, 0, 0, in, 0, 0, 0] if sel = 100
* [0, 0, 0, 0, 0, in, 0, 0] if sel = 101
* [0, 0, 0, 0, 0, 0, in, 0] if sel = 110
* [0, 0, 0, 0, 0, 0, 0, in] if sel = 111
*/
CHIP DMux8Way {
IN in, sel[3];
OUT a, b, c, d, e, f, g, h;

PARTS:
//// Replace this comment with your code.
DMux(in = in, sel = sel[2], a = temp1, b = temp2);
DMux(in = temp1, sel = sel[1], a = temp3, b = temp4);
DMux(in = temp2, sel = sel[1], a = temp5, b = temp6);
DMux(in = temp3, sel = sel[0], a = a, b = b);
DMux(in = temp4, sel = sel[0], a = c, b = d);
DMux(in = temp5, sel = sel[0], a = e, b = f);
DMux(in = temp6, sel = sel[0], a = g, b = h);
}

Step 2 A Simple ALU

以下是搭建一个简单的 ALU,相比于计组学习到的简单很多。

HalfAdder

// 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/2/HalfAdder.hdl
/**
* Computes the sum of two bits.
*/
CHIP HalfAdder {
IN a, b; // 1-bit inputs
OUT sum, // Right bit of a + b
carry; // Left bit of a + b

PARTS:
//// Replace this comment with your code.
Xor(a = a, b = b, out = sum);
And(a = a, b = b, out = carry);
}

FullAdder

// 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/2/FullAdder.hdl
/**
* Computes the sum of three bits.
*/
CHIP FullAdder {
IN a, b, c; // 1-bit inputs
OUT sum, // Right bit of a + b + c
carry; // Left bit of a + b + c

PARTS:
//// Replace this comment with your code.
Xor(a = a, b = b, out = axorb);
Xor(a = axorb, b = c, out = sum);
And(a = a, b = b, out = temp1);
And(a = a, b = c, out = temp2);
And(a = b, b = c, out = temp3);
Or(a = temp1, b = temp2, out = temp4);
Or(a = temp4, b = temp3, out = carry);
}

Add16

// 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/2/Add16.hdl
/**
* 16-bit adder: Adds two 16-bit two's complement values.
* The most significant carry bit is ignored.
*/
CHIP Add16 {
IN a[16], b[16];
OUT out[16];

PARTS:
//// Replace this comment with your code.
HalfAdder(a= a[0], b= b[0], sum= out[0], carry= t0);
FullAdder(a= a[1], b= b[1], c= t0, sum= out[1], carry= t1);
FullAdder(a= a[2], b= b[2], c= t1, sum= out[2], carry= t2);
FullAdder(a= a[3], b= b[3], c= t2, sum= out[3], carry= t3);
FullAdder(a= a[4], b= b[4], c= t3, sum= out[4], carry= t4);
FullAdder(a= a[5], b= b[5], c= t4, sum= out[5], carry= t5);
FullAdder(a= a[6], b= b[6], c= t5, sum= out[6], carry= t6);
FullAdder(a= a[7], b= b[7], c= t6, sum= out[7], carry= t7);
FullAdder(a= a[8], b= b[8], c= t7, sum= out[8], carry= t8);
FullAdder(a= a[9], b= b[9], c= t8, sum= out[9], carry= t9);
FullAdder(a= a[10], b= b[10], c= t9, sum= out[10], carry= t10);
FullAdder(a= a[11], b= b[11], c= t10, sum= out[11], carry= t11);
FullAdder(a= a[12], b= b[12], c= t11, sum= out[12], carry= t12);
FullAdder(a= a[13], b= b[13], c= t12, sum= out[13], carry= t13);
FullAdder(a= a[14], b= b[14], c= t13, sum= out[14], carry= t14);
FullAdder(a= a[15], b= b[15], c= t14, sum= out[15], carry= t15);
}

Inc16

自增

// 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/2/Inc16.hdl
/**
* 16-bit incrementer:
* out = in + 1
*/
CHIP Inc16 {
IN in[16];
OUT out[16];

PARTS:
//// Replace this comment with your code.
HalfAdder(a= in[0], b= true, sum= out[0], carry= t0);
FullAdder(a= in[1], b= false, c= t0, sum= out[1], carry= t1);
FullAdder(a= in[2], b= false, c= t1, sum= out[2], carry= t2);
FullAdder(a= in[3], b= false, c= t2, sum= out[3], carry= t3);
FullAdder(a= in[4], b= false, c= t3, sum= out[4], carry= t4);
FullAdder(a= in[5], b= false, c= t4, sum= out[5], carry= t5);
FullAdder(a= in[6], b= false, c= t5, sum= out[6], carry= t6);
FullAdder(a= in[7], b= false, c= t6, sum= out[7], carry= t7);
FullAdder(a= in[8], b= false, c= t7, sum= out[8], carry= t8);
FullAdder(a= in[9], b= false, c= t8, sum= out[9], carry= t9);
FullAdder(a= in[10], b= false, c= t9, sum= out[10], carry= t10);
FullAdder(a= in[11], b= false, c= t10, sum= out[11], carry= t11);
FullAdder(a= in[12], b= false, c= t11, sum= out[12], carry= t12);
FullAdder(a= in[13], b= false, c= t12, sum= out[13], carry= t13);
FullAdder(a= in[14], b= false, c= t13, sum= out[14], carry= t14);
FullAdder(a= in[15], b= false, c= t14, sum= out[15], carry= t15);
}

ALU

搭建 ALU 这里比较麻烦,因为zrng需要对每一位进行位运算。

这里有一个技巧,就是在计算out时,在表达式内重现命名out[0..7] out[8..15],后面可以使用进行位运算。

// 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/2/ALU.hdl
/**
* ALU (Arithmetic Logic Unit):
* Computes out = one of the following functions:
* 0, 1, -1,
* x, y, !x, !y, -x, -y,
* x + 1, y + 1, x - 1, y - 1,
* x + y, x - y, y - x,
* x & y, x | y
* on the 16-bit inputs x, y,
* according to the input bits zx, nx, zy, ny, f, no.
* In addition, computes the two output bits:
* if (out == 0) zr = 1, else zr = 0
* if (out < 0) ng = 1, else ng = 0
*/
// Implementation: Manipulates the x and y inputs
// and operates on the resulting values, as follows:
// if (zx == 1) sets x = 0 // 16-bit constant
// if (nx == 1) sets x = !x // bitwise not
// if (zy == 1) sets y = 0 // 16-bit constant
// if (ny == 1) sets y = !y // bitwise not
// if (f == 1) sets out = x + y // integer 2's complement addition
// if (f == 0) sets out = x & y // bitwise and
// if (no == 1) sets out = !out // bitwise not

CHIP ALU {
IN
x[16], y[16], // 16-bit inputs
zx, // zero the x input?
nx, // negate the x input?
zy, // zero the y input?
ny, // negate the y input?
f, // compute (out = x + y) or (out = x & y)?
no; // negate the out output?
OUT
out[16], // 16-bit output
zr, // if (out == 0) equals 1, else 0
ng; // if (out < 0) equals 1, else 0

PARTS:
//// Replace this comment with your code.
Mux16(a= x, b= false, sel= zx, out= temp1);
Not16(in= temp1, out= notx);
Mux16(a= temp1, b= notx, sel= nx, out= temp2);

Mux16(a= y, b= false, sel= zy, out= temp3);
Not16(in= temp3, out= noty);
Mux16(a= temp3, b= noty, sel= ny, out= temp4);

Add16(a = temp2, b = temp4, out = aPlusb);
And16(a= temp2, b= temp4, out= aAndb);
Mux16(a= aAndb, b= aPlusb, sel= f, out= temp5);

Not16(in= temp5, out= temp6);
Mux16(a= temp5, b= temp6, sel= no, out= out, out[0..7]= low, out[8..15]=high, out[15]= MSB);

Or8Way(in= low, out= t1);
Or8Way(in= high, out= t2);
Or(a= t1, b= t2, out = t0);
Mux(a= true, b= false, sel= t0, out= zr);
Mux(a= false, b= true, sel= MSB, out= ng);
}

Step 3 Memory

Bit

// 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/3/a/Bit.hdl
/**
* 1-bit register:
* If load is asserted, the register's value is set to in;
* Otherwise, the register maintains its current value:
* if (load(t)) out(t+1) = in(t), else out(t+1) = out(t)
*/
CHIP Bit {
IN in, load;
OUT out;

PARTS:
//// Replace this comment with your code.
Mux(a= temp, b= in, sel= load, out= temp0);
DFF(in= temp0, out= out, out= temp);
//out= out 负责输出
//out= temp 负责拉回输入
}

Register

// 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/3/a/Register.hdl
/**
* 16-bit register:
* If load is asserted, the register's value is set to in;
* Otherwise, the register maintains its current value:
* if (load(t)) out(t+1) = in(t), else out(t+1) = out(t)
*/
CHIP Register {
IN in[16], load;
OUT out[16];

PARTS:
//// Replace this comment with your code.
Bit(in= in[0], load= load, out= out[0]);
Bit(in= in[1], load= load, out= out[1]);
Bit(in= in[2], load= load, out= out[2]);
Bit(in= in[3], load= load, out= out[3]);
Bit(in= in[4], load= load, out= out[4]);
Bit(in= in[5], load= load, out= out[5]);
Bit(in= in[6], load= load, out= out[6]);
Bit(in= in[7], load= load, out= out[7]);
Bit(in= in[8], load= load, out= out[8]);
Bit(in= in[9], load= load, out= out[9]);
Bit(in= in[10], load= load, out= out[10]);
Bit(in= in[11], load= load, out= out[11]);
Bit(in= in[12], load= load, out= out[12]);
Bit(in= in[13], load= load, out= out[13]);
Bit(in= in[14], load= load, out= out[14]);
Bit(in= in[15], load= load, out= out[15]);
}

RAM8

// 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/3/a/RAM8.hdl
/**
* Memory of eight 16-bit registers.
* If load is asserted, the value of the register selected by
* address is set to in; Otherwise, the value does not change.
* The value of the selected register is emitted by out.
*/
CHIP RAM8 {
IN in[16], load, address[3];
OUT out[16];

PARTS:
//// Replace this comment with your code.

//8个对于8个编号,利用编号找到对应的Register,对应8个信号值
DMux8Way(in=load, sel=address, a=load0, b=load1, c=load2, d=load3, e=load4, f=load5, g=load6, h=load7);

Register(in=in, load=load0, out=o1);
Register(in=in, load=load1, out=o2);
Register(in=in, load=load2, out=o3);
Register(in=in, load=load3, out=o4);
Register(in=in, load=load4, out=o5);
Register(in=in, load=load5, out=o6);
Register(in=in, load=load6, out=o7);
Register(in=in, load=load7, out=o8);

Mux8Way16(a=o1, b=o2, c=o3, d=o4, e=o5, f=o6, g=o7, h=o8, sel=address, out=out);
}

RAM64

// 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/3/a/RAM64.hdl
/**
* Memory of sixty four 16-bit registers.
* If load is asserted, the value of the register selected by
* address is set to in; Otherwise, the value does not change.
* The value of the selected register is emitted by out.
*/
CHIP RAM64 {
IN in[16], load, address[6];
OUT out[16];

PARTS:
//// Replace this comment with your code.
DMux8Way(in= load, sel= address[0..2], a= load0, b= load1, c= load2, d= load3, e= load4, f= load5, g= load6, h= load7);

RAM8(in= in, load= load0, address= address[3..5], out= o0);
RAM8(in= in, load= load1, address= address[3..5], out= o1);
RAM8(in= in, load= load2, address= address[3..5], out= o2);
RAM8(in= in, load= load3, address= address[3..5], out= o3);
RAM8(in= in, load= load4, address= address[3..5], out= o4);
RAM8(in= in, load= load5, address= address[3..5], out= o5);
RAM8(in= in, load= load6, address= address[3..5], out= o6);
RAM8(in= in, load= load7, address= address[3..5], out= o7);

Mux8Way16(a= o0, b= o1, c= o2, d= o3, e= o4, f= o5, g= o6, h= o7, sel= address[0..2], out= out);
}

RAM512

// 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/3/b/RAM512.hdl
/**
* Memory of 512 16-bit registers.
* If load is asserted, the value of the register selected by
* address is set to in; Otherwise, the value does not change.
* The value of the selected register is emitted by out.
*/
CHIP RAM512 {
IN in[16], load, address[9];
OUT out[16];

PARTS:
//// Replace this comment with your code.
DMux8Way(in= load, sel= address[0..2], a= load0, b= load1, c= load2, d= load3, e= load4, f= load5, g= load6, h= load7);

RAM64(in= in, load= load0, address= address[3..8], out= o0);
RAM64(in= in, load= load1, address= address[3..8], out= o1);
RAM64(in= in, load= load2, address= address[3..8], out= o2);
RAM64(in= in, load= load3, address= address[3..8], out= o3);
RAM64(in= in, load= load4, address= address[3..8], out= o4);
RAM64(in= in, load= load5, address= address[3..8], out= o5);
RAM64(in= in, load= load6, address= address[3..8], out= o6);
RAM64(in= in, load= load7, address= address[3..8], out= o7);

Mux8Way16(a= o0, b= o1, c= o2, d= o3, e= o4, f= o5, g= o6, h= o7, sel= address[0..2], out= out);
}

RAM4K

// 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/3/b/RAM4K.hdl
/**
* Memory of 4K 16-bit registers.
* If load is asserted, the value of the register selected by
* address is set to in; Otherwise, the value does not change.
* The value of the selected register is emitted by out.
*/
CHIP RAM4K {
IN in[16], load, address[12];
OUT out[16];

PARTS:
//// Replace this comment with your code.
DMux8Way(in= load, sel= address[0..2], a= load0, b= load1, c= load2, d= load3, e= load4, f= load5, g= load6, h= load7);

RAM512(in= in, load= load0, address= address[3..11], out= o0);
RAM512(in= in, load= load1, address= address[3..11], out= o1);
RAM512(in= in, load= load2, address= address[3..11], out= o2);
RAM512(in= in, load= load3, address= address[3..11], out= o3);
RAM512(in= in, load= load4, address= address[3..11], out= o4);
RAM512(in= in, load= load5, address= address[3..11], out= o5);
RAM512(in= in, load= load6, address= address[3..11], out= o6);
RAM512(in= in, load= load7, address= address[3..11], out= o7);

Mux8Way16(a= o0, b= o1, c= o2, d= o3, e= o4, f= o5, g= o6, h= o7, sel= address[0..2], out= out);
}

RAM16K

// 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/3/b/RAM16K.hdl
/**
* Memory of 16K 16-bit registers.
* If load is asserted, the value of the register selected by
* address is set to in; Otherwise, the value does not change.
* The value of the selected register is emitted by out.
*/
CHIP RAM16K {
IN in[16], load, address[14];
OUT out[16];

PARTS:
//// Replace this comment with your code.
DMux4Way(in= load, sel= address[0..1], a= load0, b= load1, c= load2, d= load3);

RAM4K(in= in, load= load0, address= address[2..13], out= o0);
RAM4K(in= in, load= load1, address= address[2..13], out= o1);
RAM4K(in= in, load= load2, address= address[2..13], out= o2);
RAM4K(in= in, load= load3, address= address[2..13], out= o3);

Mux4Way16(a= o0, b= o1, c= o2, d= o3, sel= address[0..1], out= out);
}

PC

// 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/3/a/PC.hdl
/**
* A 16-bit counter.
* if reset(t): out(t+1) = 0
* else if load(t): out(t+1) = in(t)
* else if inc(t): out(t+1) = out(t) + 1
* else out(t+1) = out(t)
*/
CHIP PC {
IN in[16], reset, load, inc;
OUT out[16];

PARTS:
//// Replace this comment with your code.
Mux16(a= o3, b= o3add, sel= inc, out= o0);
Mux16(a= o0, b= in, sel= load, out= o1);
Mux16(a= o1, b= false, sel= reset, out= o2);

//可以先选择是哪一个为最终结果,然后巧用Register, 我们保证传递值即可
Register(in= o2, load= true, out= out, out= o3);
Inc16(in= o3, out= o3add);
}

Step 4 Machine Language

可能涉及到的符号:

  • D:数据寄存器。
  • A:数据/地址寄存器。
  • M:目前选中的寄存器。M=RAM[A]

主要涉到三种指令:

  • A:

    • 符号:为@value,表示A=value,负责设置RAM[A]RAM[value],也可能当前选中的是ROM[A]
    • RAMROM的区别除了可读可写之外,还有存放的内容RAM存放的是数据,ROM存放的是指令。
  • 二进制:为0value,最高位表示操作码,剩下的十五位表示value

  • M:M表示当前选定的RAM[A]

  • C:

    • 符号:dest = comp; jumpdest表示目的操作数,comp表示计算结果,jump表示跳转指令选项。dest = comp将计算结果赋值给dest

基础语法:

  • reg:0、-1、1。表示寄存器只能赋值这三个值。
  • reg1=reg2:其中reg1={A|D|M}reg2=[-]{A|D|M}
  • reg = reg1 op reg2:其中reg,reg1={A|D|M}reg2={A|D|M|1|0|-1}op={+|-|&||}

控制:使用 A 指令选择一个地址,使用 C 指令进行跳转。常见类型如下:

e. g

// if(D>0) goto 6
@6
D;JGT

变量:使用语法@sym:表示将一个数字i(随机分配的)对应的RAM[i]命名为sym

以下有一个内置变量的表格:包括 16 个虚拟内存器,2 个用于 IO 设备,6 个用于虚拟机和编译器。

symbol value
R0 0
R1 1
R2 2
... ...
R15 15
SCREEN 16384
KBD 24576
SP 0
LCL 1
ARG 2
THIS 3
THAT 4

标签:为一段代码定义一个标签,可以执行跳转功能,负责实现 if - else 和 循环语句。

其中(label)主要是标记跳转的地方的开始,@label表示的是执行跳转,如果满足条件。

指针:指针是指向变量的地址。这里使用数组为例,格式如下:

可以发现:是通过A=...实现的,因为M=RAM[A],利用这个特性,可以实现指针访问数组。

另外一个案例:

建议

  1. 最好以以下格式为结尾,防止发生NOP

    @value
    0;JMP

    有一个对应上面的例子:

  2. 为了提高可读性,使用代码 2 代替代码 1:

    // Code 1
    //RAM[5]=15
    @15
    D=A

    @5
    M=D

    // Code 2
    //RAM[5]=15
    @15
    D=A

    @R5
    M=D

Step 5 Hack Computer

Memory

以下是电路图:

实现原理和前面的 RAM 同理,主要是注意需要使用ScreenKeyBoard

这里使用 Mux4Way16,可以划分为四路,前两路对应的是 RAM16K,后两路对应的分别是 Screen 和 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/5/Memory.hdl
/**
* The complete address space of the Hack computer's memory,
* including RAM and memory-mapped I/O.
* The chip facilitates read and write operations, as follows:
* Read: out(t) = Memory[address(t)](t)
* Write: if load(t-1) then Memory[address(t-1)](t) = in(t-1)
* In words: the chip always outputs the value stored at the memory
* location specified by address. If load=1, the in value is loaded
* into the memory location specified by address. This value becomes
* available through the out output from the next time step onward.
* Address space rules:
* Only the upper 16K+8K+1 words of the Memory chip are used.
* Access to address>0x6000 is invalid and reads 0. Access to any address
* in the range 0x4000-0x5FFF results in accessing the screen memory
* map. Access to address 0x6000 results in accessing the keyboard
* memory map. The behavior in these addresses is described in the Screen
* and Keyboard chip specifications given in the lectures and the book.
*/
CHIP Memory {
IN in[16], load, address[15];
OUT out[16];

PARTS:
//// Replace this comment with your code.
DMux4Way(in= load, sel= address[13..14], a= temp0, b= temp1, c= load1, d= load2);
Or(a= temp0, b= temp1, out= load0);
RAM16K(in= in, load= load0, address= address[0..13], out= o1);
Keyboard(out= o3);
Screen(in= in, load= load1, address= address[0..12], out= o2);
Mux4Way16(a= o1, b= o1, c= o2, d= o3, sel= address[13..14], out= out);
}

Computer

以下是电路实现图:

// 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/5/Computer.hdl
/**
* The Hack computer, consisting of CPU, ROM and RAM.
* When reset = 0, the program stored in the ROM executes.
* When reset = 1, the program's execution restarts.
* Thus, to start running the currently loaded program,
* set reset to 1, and then set it to 0.
* From this point onwards, the user is at the mercy of the software.
* Depending on the program's code, and whether the code is correct,
* the screen may show some output, the user may be expected to enter
* some input using the keyboard, or the program may do some procerssing.
*/
CHIP Computer {

IN reset;

PARTS:
//// Replace this comment with your code.
ROM32K(address= next, out= ins);
CPU(inM= num, instruction= ins, reset= reset, outM= oM, writeM= wM, addressM= aM, pc= next);
Memory(in= oM, load= wM, address= aM, out= num);
}

CPU

最复杂的部分,需要对每个状态位的功能进行分析。

// 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/5/CPU.hdl
/**
* The Hack Central Processing unit (CPU).
* Parses the binary code in the instruction input and executes it according to the
* Hack machine language specification. In the case of a C-instruction, computes the
* function specified by the instruction. If the instruction specifies to read a memory
* value, the inM input is expected to contain this value. If the instruction specifies
* to write a value to the memory, sets the outM output to this value, sets the addressM
* output to the target address, and asserts the writeM output (when writeM = 0, any
* value may appear in outM).
* If the reset input is 0, computes the address of the next instruction and sets the
* pc output to that value. If the reset input is 1, sets pc to 0.
* Note: The outM and writeM outputs are combinational: they are affected by the
* instruction's execution during the current cycle. The addressM and pc outputs are
* clocked: although they are affected by the instruction's execution, they commit to
* their new values only in the next cycle.
*/
CHIP CPU {

IN inM[16], // M value input (M = contents of RAM[A])
instruction[16], // Instruction for execution
reset; // Signals whether to re-start the current
// program (reset==1) or continue executing
// the current program (reset==0).

OUT outM[16], // M value output
writeM, // Write to M?
addressM[15], // Address in data memory (of M)
pc[15]; // address of next instruction

PARTS:
//// Replace this comment with your code.

//first Mux16
//c=0 instruction
//c=1 ALUout
Mux16(a= instruction, b= ALUout, sel= instruction[15], out= Mout1);

//because when c=0, A-instruction will set A register to xxx
//so when c=0, we need to set load(A) is true
Not(in= instruction[15], out= notOp);

//when c=1, we need to focus on ins[5].., so we will get ins[5]..
Or(a= instruction[5], b= notOp, out= load0);

//A register
Register(in= Mout1, load= load0, out= Aout, out[0..14]= addressM);

//for C-instruction many-bits need calculate
//for second Mux16
And(a= instruction[15], b= instruction[12], out= load1);

//for D register
And(a= instruction[15], b= instruction[4], out= load2);

//for writeM
And(a= instruction[15], b= instruction[3], out= writeM);

//D register
Register(in= ALUout, load= load2, out= Dout);

//Second Mux16
Mux16(a= Aout, b= inM, sel= load1, out= Mout2);

//ALU
//because when c=0 load2=0,D register makes no change
//so we don't need to test c=0 or c=1 then test ins[11..6]
ALU(x= Dout, y= Mout2, zx= instruction[11], nx= instruction[10], zy= instruction[9], ny= instruction[8], f= instruction[7], no= instruction[6], out= outM, out= ALUout, zr= Azero, ng= Ang);

//PC
Or(a= Azero, b= Ang, out= sym);
Not(in= sym, out= Positive);

//out > 0 can JMP
And(a= Positive, b= instruction[0], out= isPositive);

//out == 0 can JMP
And(a= Azero, b= instruction[1], out= isZero);

//out < 0 can JMP
And(a= Ang, b= instruction[2], out= lessZero);

//no condition can JMP
And(a= instruction[2], b=instruction[1], out= t12);
And(a= instruction[0], b=t12, out= temp);

Or(a= isPositive, b= isZero, out= temp1);
Or(a= temp1, b= lessZero, out= temp2);
Or(a= temp2, b= temp, out= temp3);

And(a= temp3, b= instruction[15], out= condition);
PC(in= Aout, load= condition, inc= true, reset= reset, out[0..14]=pc);
}

Step 6 Assembler

按照书上的 API,编写的各个类结构如下:

Code 类

+-----------------------+
| Code |
+-----------------------+
| - destLib: map<string, string> |
| - compLib: map<string, string> |
| - jumpLib: map<string, string> |
+-----------------------+
| + Code() |
| + ~Code() |
| + dest(para_string: string): string|
| + comp(para_string: string): string|
| + jump(para_string: string): string|
+-----------------------+

//
// Created by APP on 24-10-19.
//

#ifndef CODE_H
#define CODE_H
#include<string>
#include<map>
class Code {
public:
Code();
~Code();
std::string dest(const std::string &para_string); //返回dest助记符的二进制码
std::string comp(const std::string &para_string); //返回comp助记符的二进制码
std::string jump(const std::string &para_string); //返回jump助记符的二进制码
private:
//三个对应的map,用于存储助记符对应的二进制码
std::map<std::string, std::string> destLib, compLib, jumpLib;
};



#endif //CODE_H

//
// Created by APP on 24-10-19.
//

#include "Code.h"

#include <pthread_compat.h>

Code::Code() {
//init destLib
destLib["null"] = "000";
destLib["M"] = "001";
destLib["D"] = "010";
destLib["MD"] = "011";
destLib["A"] = "100";
destLib["AM"] = "101";
destLib["AD"] = "110";
destLib["AMD"] = "111";

//init comp
compLib["0"] = "0101010";
compLib["1"] = "0111111";
compLib["-1"] = "0111010";
compLib["D"] = "0001100";
compLib["A"] = "0110000";
compLib["M"] = "1110000";
compLib["!D"] = "0001101";
compLib["!A"] = "0110001";
compLib["!M"] = "1110001";
compLib["-D"] = "0001111";
compLib["-A"] = "0110011";
compLib["-M"] = "1110011";
compLib["D+1"] = "0011111";
compLib["A+1"] = "0110111";
compLib["M+1"] = "1110111";
compLib["D-1"] = "0001110";
compLib["A-1"] = "0110010";
compLib["M-1"] = "1110010";
compLib["D+A"] = "0000010";
compLib["D+M"] = "1000010";
compLib["D-A"] = "0010011";
compLib["D-M"] = "1010011";
compLib["A-D"] = "0000111";
compLib["M-D"] = "1000111";
compLib["D&A"] = "0000000";
compLib["D&M"] = "1000000";
compLib["D|A"] = "0010101";
compLib["D|M"] = "1010101";

//init jump
jumpLib["null"] = "000";
jumpLib["JGT"] = "001";
jumpLib["JEQ"] = "010";
jumpLib["JGE"] = "011";
jumpLib["JLT"] = "100";
jumpLib["JNE"] = "101";
jumpLib["JLE"] = "110";
jumpLib["JMP"] = "111";
}

Code::~Code() = default;

std::string Code::dest(const std::string &para_string) {
return destLib[para_string];
}

std::string Code::comp(const std::string &para_string) {
return compLib[para_string];
}

std::string Code::jump(const std::string &para_string) {
return jumpLib[para_string];
}

SymbolTable 类

+----------------------------+
| SymbolTable |
+----------------------------+
| - tot: int = 23 |
| - symbolLib: map<string, int>|
| - usedValues: set<int> |
| - start: int = 16 |
+----------------------------+
| + constructor() |
| + addEntry(para_string: string, address: int) |
| + contains(para_string: string): bool |
| + getAddress(para_string: string): int |
+----------------------------+

//
// Created by APP on 24-10-19.
//

#ifndef SYMBOLTABLE_H
#define SYMBOLTABLE_H

#include<string>
#include<map>
#include<set>

class SymbolTable {
public:
void constructor(); //制订空的符号表
void addEntry(const std::string &para_string, const int &address); //将(symbol, address)配对加入到符号表中
[[nodiscard]] bool contains(const std::string &para_string) const; //符号表中是否包含了指定的symbol
int getAddress(const std::string &para_string); //返回与symbol相关的地址
private:
int tot = 23; //记录总共的symbol数目
std::map<std::string, int> symbolLib; //存储符号表
std::set<int> usedValues; //记录已经使用过的地址
int start = 16; //记录最后一个使用过的地址,按照从小到大的顺序
};



#endif //SYMBOLTABLE_H

//
// Created by APP on 24-10-19.
//

#include "SymbolTable.h"
#include <set>

void SymbolTable::constructor() {
symbolLib["SP"] = 0;
symbolLib["LCL"] = 1;
symbolLib["ARG"] = 2;
symbolLib["THIS"] = 3;
symbolLib["THAT"] = 4;
symbolLib["SCREEN"] = 16384;
symbolLib["KBD"] = 24576;
for (int i = 0; i < 16; ++i) {
std::string num = std::to_string(i);
std::string str = "R" + num;
symbolLib[str] = i;
}

//存储已经使用过的address
for (auto &[str, value]: symbolLib) {
usedValues.insert(value);
}
}

bool SymbolTable::contains(const std::string &para_string) const{
return symbolLib.count(para_string);
}

void SymbolTable::addEntry(const std::string &para_string, const int &address) {
if(address != -1) {
symbolLib[para_string] = address;
tot++;
}
else {
while (usedValues.count(start)) {
start++;
}
symbolLib[para_string] = start;
usedValues.insert(start);
tot++;
}
}

int SymbolTable::getAddress(const std::string &para_string) {
return symbolLib[para_string];
}

Praser 类

+----------------------------------+
| Praser |
+----------------------------------+
| + inputs: vector<string> |
| + current_command: string |
| + current_id: int = -1 |
| + count: int = 0 |
+----------------------------------+
| + init(file_name: string): void |
| + hasMoreCommands(): bool |
| + advance(): void |
| + commandType(): char |
| + symbol(): string |
| + dest(): string |
| + comp(): string |
| + jump(): string |
| + reset(): void |
+----------------------------------+

//
// Created by APP on 24-10-19.
//

#ifndef PRASER_H
#define PRASER_H

#include <queue>
#include<string>

class Praser {
public:
void init(const std::string &file_name); //对文件进行初步读取:删去空行,空格,注释,打开文件输入流,为语法解析做准备
[[nodiscard]]bool hasMoreCommands() const; //确认输入中是否还有更多命令
void advance(); //从输入中读取下一条指令,将其当作“当前指令”,当且仅当hasMoreCommandS()为真时
[[nodiscard]]char commandType() const; //返回指令类型:A:@ L: () C: dest=comp;jump
[[nodiscard]]std::string symbol() const; //返回指令为A或L类型时,当前命令的符号和十进制值
[[nodiscard]]std::string dest() const; //返回当前C指令的dest助记符
[[nodiscard]]std::string comp() const; //返回当前C指令的comp助记符
[[nodiscard]]std::string jump() const; //返回当前C指令的jump助记符
void reset(); //重置
std::vector<std::string> inputs; //用来存储输入的指令
std::string current_command; //当前正在处理的指令
int current_id = -1; //当前指令的id
int count = 0; //记录总共有多少条指令
};



#endif //PRASER_H

//
// Created by APP on 24-10-19.
//

#include "Praser.h"
#include<fstream>
#include<iostream>
#include <string>
#include <algorithm>
void Praser::init(const std::string &filename) {
std::ifstream file(filename);
//判断是否能够打开文件
if(!file.is_open()) {
std::cerr << "error! can not open the file";
}
std::string line;
//使用getline进行逐行读取指令
while(std::getline(file, line)) {
if(line.empty()) { //空指令,删去
continue;
}
std::string new_line; //删去指令中的空格符号
for (auto ch: line) {
if(ch != ' ') {
new_line += ch;
}
}
size_t pos = new_line.find("//"); //删去指令中的注释,保留该行中注释前面的内容
if(pos == std::string::npos) {
std::string str = new_line.substr(0, pos);
new_line = str;
inputs.emplace_back(new_line);
count++;
}
}

file.close();
}

bool Praser::hasMoreCommands() const{
return current_id < count - 1;
}

void Praser::advance() {
if(hasMoreCommands()) current_command = inputs[++current_id];
}

char Praser::commandType() const {
if(current_command[0] == '@') return 'A';
else if(current_command[0] == '(') return 'L';
else {
return 'C';
}
}

std::string Praser::symbol() const{
if(current_command[0] == '@') { //读取A指令对应的符号
std::string str = current_command.substr(1, current_command.size() - 1);
return str;
}
else { //读取L指令对应的符号
size_t pos_next = current_command.find(')');
std::string str = current_command.substr(1, pos_next - 1);
return str;
}
}


std::string Praser::dest() const{
size_t pos = current_command.find('=');
if(pos == std::string::npos) {
return "null";
}
std::string str = current_command.substr(0, pos);
return str;
}

std::string Praser::comp() const{
//对应三种情况:
//dest jump都有
//dest jump其中之一有
//两者都无
size_t pos_eq = current_command.find('=');
size_t pos_sc = current_command.find(';');
if(pos_eq != std::string::npos && pos_sc != std::string::npos) {
std::string str = current_command.substr(pos_eq + 1, pos_sc - pos_eq - 1);
return str;
}
else if(pos_eq != std::string::npos && pos_sc == std::string::npos) {
std::string str = current_command.substr(pos_eq + 1);
return str;
}
else if(pos_eq == std::string::npos && pos_sc != std::string::npos) {
std::string str = current_command.substr(0, pos_sc);
return str;
}
else {
return current_command;
}
}

std::string Praser::jump() const{
size_t pos_sc = current_command.find(';');
if(pos_sc != std::string::npos) {
std::string str = current_command.substr(pos_sc + 1);
return str;
}
else {
return "null";
}
}

void Praser::reset() {
current_command = "";
current_id = -1;
}

test 类

自己编写的测试类

+----------------------------------------+
| test |
+----------------------------------------+
| - binaryfile: vector<string> |
+----------------------------------------+
| + init(filename: string): void |
| + findDifference(compare_file: vector<string>): void |
+----------------------------------------+
//
// Created by APP on 24-10-20.
//

#ifndef TEST_H
#define TEST_H
#include <vector>
#include <string>

class test {
public:
void init(const std::string &filename); //初始化,读取hack文件
void findDifference(const std::vector<std::string> &compare_file) const; //进行文件的比较
private:
std::vector<std::string> binaryfile;
};



#endif //TEST_H

//
// Created by APP on 24-10-20.
//

#include "test.h"
#include <iostream>
#include <fstream>

void test::init(const std::string &filename) {
std::ifstream file(filename);
if(!file.is_open()) {
std::cerr << "error! can not open the file";
}
std::string line;
while(std::getline(file, line)) {
binaryfile.emplace_back(line);
}
}

void test::findDifference(const std::vector<std::string> &compare_file) const{
bool ok = true;
for (int i = 0; i < binaryfile.size(); ++i) {
if(binaryfile[i] != compare_file[i]) {
ok = false;
std::cerr << "error! string " << i << " is different\n";
std::cerr << "binary[" << i << "] is " << binaryfile[i] << "\n";
std::cerr << "compare_file[" << i << "] is " << compare_file[i] << "\n";
}
}
if(ok) std::cout << "success!\n";
}

main

#include <bits/stdc++.h>
#include "Code.h"
#include "Praser.h"
#include "SymbolTable.h"
#include "test.h"

int main() {
Praser praser;
Code code;
SymbolTable symbol_table;

test machine;

std::cout << "binary file\n";
std::string fileName = "../AllTest/Add.asm";
std::string checkName = "../AllTest/Add.hack";
machine.init(checkName);

praser.init(fileName);
symbol_table.constructor();
std::vector<std::string> tempfile;
int count = 0; //关键变量:用于记录当前扫描到第几行
// 构建符号表:先扫描L指令:添加到symbol_table中,并忽略,其余指令正常添加到处理文件中
while(praser.hasMoreCommands()) {
praser.advance();
char commandType = praser.commandType();
if(commandType == 'L') {
std::string new_symbol = praser.symbol();
if(symbol_table.contains(new_symbol) == false) {
symbol_table.addEntry(new_symbol, count);
std::string str = praser.current_command;
size_t pos = str.find(')');
std::string left = str.substr(pos + 1);
if(left.empty() == false) { //判断L指令后面是否还有其他的指令:如A指令,C指令
tempfile.emplace_back(left);
count++;
}
}
}
else {
std::string str = praser.current_command;
tempfile.emplace_back(str);
count++;
}
}
praser.reset();
praser.inputs = tempfile;
std::vector<std::string> binaryfile;
std::string temp_varible;
bool ok = true;
while(praser.hasMoreCommands()) { //对变量进行处理
praser.advance();
char commandType = praser.commandType();
if(commandType == 'A') {
std::string sym = praser.symbol();
if(sym[0] >= '0' && sym[0] <= '9') { //如果是@number类型,直接跳过
continue;
}
else if(symbol_table.contains(sym) == true) { //如果在symbol_table找到了,直接修改当前指令
praser.inputs[praser.current_id] = '@' + std::to_string(symbol_table.getAddress(sym));
}
else {
symbol_table.addEntry(sym, -1); //否则检查最新的最小可用地址,添加到symbol_table中
}
}
}
praser.reset();
while(praser.hasMoreCommands()) { //最终转化为二级制代码
praser.advance();
char commandType = praser.commandType();
if(commandType == 'A') {
std::string str = "0";
std::string tmp = praser.current_command.substr(1);
if(symbol_table.contains(tmp)) { //对应符号的情况:先查找地址,再转化为二进制字符串
int address = symbol_table.getAddress(tmp);
std::string binary;
if (address == 0) binary = "000000000000000";
while (address > 0) {
binary.insert(0, std::to_string(address % 2)); // 将二进制位插入到字符串前面
address /= 2; // 除以2,继续处理
}
while(binary.size() < 15) {
binary.insert(0,std::to_string(0));
}
str += binary;
}
else { //对应数字的情况:直接转化为二级制字符串
int number = std::stoi(tmp);
std::string binary;
if (number == 0) binary = "000000000000000";
while (number > 0) {
binary.insert(0, std::to_string(number % 2)); // 将二进制位插入到字符串前面
number /= 2; // 除以2,继续处理
}
while(binary.size() < 15) {
binary.insert(0,std::to_string(0));
}
str += binary;
}
binaryfile.emplace_back(str);
}
else if(commandType == 'C') { //对应C指令的情况:先查找符号表,后转化为二进制字符
std::string str = "111";
str += code.comp(praser.comp());
str += code.dest(praser.dest());
str += code.jump(praser.jump());
binaryfile.emplace_back(str);
}
}
// for (auto &str: binaryfile) {
// std::cout << str << "\n";
// }

machine.findDifference(binaryfile);
return 0;
}

Step 7 Virtual machine Part1

按照书上的 API,编写的各个类如下:

Praser 类

+----------------------+
| Praser |
+----------------------+
| - curremt_command : std::string |
| - current_id : int |
| - inputs : std::pmr::vector<std::string>|
| - count : int |
+----------------------+
| + init(filename : const std::string&) |
| + hasMoreCommands() : bool |
| + advance() : void |
| + command_type() : C_TYPE |
| + arg1() : std::string |
| + arg2() : std::string |
+----------------------+
| enum C_TYPE |
| - C_ARITHMETIC |
| - C_PUSH |
| - C_POP |
| - C_LABEL |
| - C_GOTO |
| - C_IF |
| - C_FUNCTION |
| - C_RETRUN |
| - C_CALL |
| - C_ERROR |
+----------------------+
//
// Created by APP on 24-10-21.
//

#ifndef PRASER_H
#define PRASER_H

#include <string>
#include <vector>

class Praser {
public:
enum C_TYPE {
C_ARITHMETIC,
C_PUSH,
C_POP,
C_LABEL,
C_GOTO,
C_IF,
C_FUNCTION,
C_RETRUN,
C_CALL,
C_ERROR
};
void init(const std::string &filename);
[[nodiscard]]bool hasMoreCommands() const;
void advance();
[[nodiscard]]C_TYPE command_type() const;
std::string arg1();
std::string arg2();
private:
std::string curremt_command;
int current_id = -1;
std::pmr::vector<std::string> inputs;
int count = 0;
};



#endif //PRASER_H

//
// Created by APP on 24-10-21.
//

#include "Praser.h"
#include <fstream>
#include <iostream>

void Praser::init(const std::string &filename) {
std::ifstream file(filename + ".vm");
if(!file.is_open()) {
std::cerr << "error! cann't find file\n";
return;
}
std::string line;
while(std::getline(file, line)) {
if(line.empty()) {
continue;
}
std::string new_line;
std::string str;
bool ok = false;
for (auto ch: line) {
if(ch != ' ') {
str += ch;
if(ok == false) ok = true;
}
else {
if(ok == true) {
ok = false;
new_line += str + ';';
str = "";
}
}
}
if(str.empty() == false) {
new_line += str;
}
size_t pos = new_line.find("//");
if(pos == std::string::npos) {
std::cout << new_line << std::endl;
inputs.emplace_back(new_line);
count++;
}
else {
if(pos > 0) {
new_line = new_line.substr(0, pos);
inputs.emplace_back(new_line);
count++;
}
}
}
file.close();
}

bool Praser::hasMoreCommands() const{
return current_id < count - 1;
}


void Praser::advance() {
if(hasMoreCommands()) curremt_command = inputs[++current_id];
}

Praser::C_TYPE Praser::command_type() const{
size_t pos = curremt_command.find(';');
std::string str = curremt_command.substr(0, pos);
if(str == "add" || str == "sub" || str == "neg" || str == "eq" || str == "gt" || str == "lt" || str == "and" || str == "or" || str == "not") {
return C_ARITHMETIC;
}
else if(str == "pop") {
return C_POP;
}
else if(str == "push") {
return C_PUSH;
}
else {
std::cerr << "C_TYPE: " << str << std::endl;
std::cerr << "error! C_TYPE cann't be found \n";
return C_ERROR;
}
}

std::string Praser::arg1() {
if(command_type() == C_ARITHMETIC) {
return curremt_command;
}
else if(command_type() == C_RETRUN) {

}
else {
size_t pos1 = curremt_command.find(';');
size_t pos2 = 0;
for (size_t i = pos1 + 1; i < curremt_command.size(); ++i) {
if(curremt_command[i] == ';') {
pos2 = i;
break;
}
}
std::string str = curremt_command.substr(pos1 + 1, pos2 - pos1 - 1);
return str;
}
}

std::string Praser::arg2() {
if(command_type() == C_PUSH || command_type() == C_POP || command_type() == C_CALL || command_type() == C_FUNCTION) {
size_t pos1 = curremt_command.find(';');
size_t pos2 = 0;
for (size_t i = pos1 + 1; i < curremt_command.size(); ++i) {
if(curremt_command[i] == ';') {
pos2 = i;
break;
}
}
std::string str = curremt_command.substr(pos2 + 1);
return str;
}
}

CodeWriter 类

+--------------------------+
| CodeWriter |
+--------------------------+
| - file : std::ofstream |
| - staticLabel : std::string |
| - lib : std::map<std::string, std::string> |
| - eq_i : int |
| - gt_i : int |
| - lt_i : int |
+--------------------------+
| + write(filename : const std::string&) : void |
| + writeArithmetic(command_type : const std::string&) : void |
| + writePushPop(command_type : const std::string&, segment : const std::string&, index : const std::string&) : void |
| + close() : void |
+--------------------------+
//
// Created by APP on 24-10-21.
//

#ifndef CODEWRITER_H
#define CODEWRITER_H
#include <string>
#include <vector>
#include <fstream>
#include <map>

class CodeWriter {
public:
void write(const std::string &filename);
void writeArithmetic(const std::string &command_type);
void writePushPop(const std::string &command_type, const std::string &segment, const std::string &index);
void close();
private:
std::ofstream file;
std::string staticLabel;
std::map<std::string, std::string> lib ={
{"local", "LCL"},
{"argument", "ARG"},
{"this", "THIS"},
{"that", "THAT"}
};
int eq_i = 0;
int gt_i = 0;
int lt_i = 0;
};



#endif //CODEWRITER_H

//
// Created by APP on 24-10-21.
//

#include "CodeWriter.h"
#include <iostream>
void CodeWriter::write(const std::string &filename) {
file.open(filename + ".asm");
size_t pos = filename.rfind('/');
if(pos != std::string::npos) {
staticLabel = filename.substr(pos + 1);
}
else {
staticLabel = filename;
}
if(file.is_open() == false) {
std::cerr << "error! cann't find the file";
}
}

void CodeWriter::close() {
file.close();
}

void CodeWriter::writeArithmetic(const std::string &command_type) {
if(command_type == "add") {
//取出两个元素,进行加法运算,最后加入到栈中
file << "@SP" << std::endl;
file << "AM=M-1" << std::endl; //更新SP
file << "D=M" << std::endl; //栈顶元素
file << "A=A-1" << std::endl; //第二个元素
file << "M=D+M" << std::endl; //进行加法运算
}
else if(command_type == "sub") {
file << "@SP" << std::endl;
file <<"AM=M-1" << std::endl;
file << "D=M" << std::endl;
file << "A=A-1" << std::endl;
file << "M=M-D" << std::endl;
}
else if(command_type == "neg") {
file << "@SP" << std::endl;
file << "A=M-1" << std::endl;
file << "M=-M" << std::endl;
}
else if(command_type == "eq") {
file << "@SP" << std::endl;
file << "AM=M-1" << std::endl;
file << "D=M" << std::endl;
file << "A=A-1" << std::endl;
file << "D=M-D" << std::endl;
file << "M=-1" << std::endl;
file << "@EQ_" + std::to_string(eq_i) << std::endl;
file << "D;JEQ" << std::endl;
file << "@SP" << std::endl;
file << "A=M-1" << std::endl;
file << "M=0" << std::endl;
file << "(EQ_" + std::to_string(eq_i) + ")" << std::endl;
++eq_i;
}
else if(command_type == "gt") {
file << "@SP" << std::endl;
file << "AM=M-1" << std::endl;
file << "D=M" << std::endl;
file << "A=A-1" << std::endl;
file << "D=M-D" << std::endl;
file << "M=-1" << std::endl;
file << "@GT_" + std::to_string(gt_i) << std::endl;
file << "D;JGT" << std::endl;
file << "@SP" << std::endl;
file << "A=M-1" << std::endl;
file << "M=0" << std::endl;
file << "(GT_" + std::to_string(gt_i) + ")" << std::endl;
++gt_i;
}
else if(command_type == "lt") {
file << "@SP" << std::endl;
file << "AM=M-1" << std::endl;
file << "D=M" << std::endl;
file << "A=A-1" << std::endl;
file << "D=M-D" << std::endl;
file << "M=-1" << std::endl;
file << "@LT_" + std::to_string(lt_i) << std::endl;
file << "D;JLT" << std::endl;
file << "@SP" << std::endl;
file << "A=M-1" << std::endl;
file << "M=0" << std::endl;
file << "(LT_" + std::to_string(lt_i) + ")" << std::endl;
++lt_i;
}
else if(command_type == "and") {
file << "@SP" << std::endl;
file << "AM=M-1" << std::endl;
file << "D=M" << std::endl;
file << "A=A-1" << std::endl;
file << "M=D&M" << std::endl;
}
else if(command_type == "or") {
file << "@SP" << std::endl;
file << "AM=M-1" << std::endl;
file << "D=M" << std::endl;
file << "A=A-1" << std::endl;
file << "M=D|M" << std::endl;
}
else if(command_type == "not") {
file << "@SP" << std::endl;
file << "A=M-1" << std::endl;
file << "M=!M" << std::endl;
}
}

void CodeWriter::writePushPop(const std::string &command_type, const std::string &segment, const std::string &index) {
if(command_type == "push") {
if(segment == "constant") {
file << "@" + index << std::endl;
file << "D=A" << std::endl;
}
else if(lib.count(segment)) {
file << "@" + index << std::endl;
file << "D=A" << std::endl;
file << "@" + lib[segment] << std::endl;
file << "A=D+M" << std::endl;
file << "D=M" << std::endl;
}
else if(segment == "pointer") {
file << "@" + index << std::endl;
file << "D=A" << std::endl;
file << "@3" << std::endl;
file << "A=D+A" << std::endl;
file << "D=M" << std::endl;
}
else if(segment == "temp") {
file << "@" + index << std::endl;
file << "D=A" << std::endl;
file << "@5" << std::endl;
file << "A=D+A" << std::endl;
file << "D=M" << std::endl;
}
else if(segment == "static") {
file << "@" + staticLabel + "." + index << std::endl;
file << "D=M" << std::endl;
}
else {
//error
}
//加入栈中,更新SP
file << "@SP" << std::endl;
file << "A=M" << std::endl;
file << "M=D" << std::endl;
file << "@SP" << std::endl;
file << "M=M+1" << std::endl;
}
else if(command_type == "pop") {
if(lib.count(segment)) {
file << "@" + index << std::endl;
file << "D=A" << std::endl;
file << "@" + lib[segment] << std::endl;
file << "D=D+M" << std::endl;
}
else if(segment == "pointer") {
file << "@" + index << std::endl;
file << "D=A" << std::endl;
file << "@3" << std::endl;
file << "D=D+A" << std::endl;
}
else if(segment == "temp") {
file << "@" + index << std::endl;
file << "D=A" << std::endl;
file << "@5" << std::endl;
file << "D=D+A" << std::endl;
}
else if(segment == "static") {
file << "@" + staticLabel + "." + index << std::endl;
file << "D=A" << std::endl;
}
//弹出栈,更新SP,需要多使用一个寄存器,不然无法实现
file << "@R13" << std::endl;
file << "M=D" << std::endl;
file << "@SP" << std::endl;
file << "AM=M-1" << std::endl;
file << "D=M" << std::endl;
file << "@R13" << std::endl;
file << "A=M" << std::endl;
file << "M=D" << std::endl;
}
}

VMtranslater 类

+---------------------------+
| VMtranslater |
+---------------------------+
| - praser : Praser |
| - code_writer : CodeWriter|
+---------------------------+
| + VMtranslater() |
| + convert(filename : const std::string&) : void |
+---------------------------+

//
// Created by APP on 24-10-22.
//

#ifndef VMTRANSLATER_H
#define VMTRANSLATER_H

#include "Praser.h"
#include "CodeWriter.h"


class VMtranslater {
public:
VMtranslater() = default;
void convert(const std::string &filename);
private:
Praser praser;
CodeWriter code_writer;

};


#endif //VMTRANSLATER_H

//
// Created by APP on 24-10-22.
//

#include "VMtranslater.h"
#include <iostream>

void VMtranslater::convert(const std::string &filename) {
praser.init(filename);
code_writer.write(filename);

while(praser.hasMoreCommands()) {
praser.advance();
if(praser.command_type() == Praser::C_ARITHMETIC) {
std::string arg = praser.arg1();
code_writer.writeArithmetic(arg);
}
else if(praser.command_type() == Praser::C_POP) {
std::string arg1 = praser.arg1();
std::string arg2 = praser.arg2();
code_writer.writePushPop("pop", arg1, arg2);
}
else if(praser.command_type() == Praser::C_PUSH) {
std::string arg1 = praser.arg1();
std::string arg2 = praser.arg2();
code_writer.writePushPop("push", arg1, arg2);
}
else {
std::cerr << "undefined C_TYPE" << praser.command_type() << std::endl;
}
}
code_writer.close();
std::cout << "finish write" << std::endl;
}


main

#include <iostream>
#include "VMtranslater.h"

int main()
{
VMtranslater my_translater;
std::string filename = "../AllTest/MemoryAccess/StaticTest/StaticTest";
// std::cout << "Please input your file name:\n";
// std::cin >> filename;
my_translater.convert(filename);
return 0;
}

Step 8 Virtual Machine Part2

Parser 类

+------------------------------------+
| Parser |
+------------------------------------+
| - twoArgs : std::map<C_TYPE, std::string> |
| - oneArgs : std::map<C_TYPE, std::string> |
| - curremt_command : std::string |
| - inputs : std::vector<std::string> |
| - current_id : int |
| - count : int |
+------------------------------------+
| + init(filename : const fs::path&) : void |
| + reset() : void |
| + hasMoreCommands() : bool |
| + advance() : void |
| + command_type() : C_TYPE |
| + arg1() : std::string |
| + arg2() : std::string |
+------------------------------------+
| enum C_TYPE |
| - C_ARITHMETIC |
| - C_PUSH |
| - C_POP |
| - C_LABEL |
| - C_GOTO |
| - C_IF |
| - C_FUNCTION |
| - C_RETRUN |
| - C_CALL |
| - C_ERROR |
+------------------------------------+

//
// Created by APP on 24-10-23.
//

#ifndef PARSER_H
#define PARSER_H

#include <string>
#include <filesystem>
#include <vector>
#include <map>

namespace fs = std::filesystem;
class Parser {
public:
enum C_TYPE {
C_ARITHMETIC,
C_PUSH,
C_POP,
C_LABEL,
C_GOTO,
C_IF,
C_FUNCTION,
C_RETRUN,
C_CALL,
C_ERROR
};
void init(const fs::path &filename);
void reset();
[[nodiscard]]bool hasMoreCommands() const;
void advance();
[[nodiscard]]C_TYPE command_type() const;
std::string arg1();
std::string arg2();

private:
std::map<Parser::C_TYPE, std::string> twoArgs = {
{C_FUNCTION, "C_FUNCTION"},
{C_CALL, "C_CALL"},
{C_PUSH, "C_PUSH"},
{C_POP, "C_POP"}
};
std::map<Parser::C_TYPE, std::string> oneArgs = {
{C_LABEL, "C_LABEL"},
{C_GOTO, "C_GOTO"},
{C_IF, "C_IF"}
};
std::string curremt_command;
std::vector<std::string> inputs;
int current_id = -1;
int count = 0;
};



#endif //PARSER_H

//
// Created by APP on 24-10-23.
//

#include "Parser.h"
#include <iostream>
#include <filesystem>
#include <fstream>
namespace fs =std::filesystem;

void Parser::init(const fs::path &filename) {
std::ifstream file(filename);
if(file.is_open() == false) {
std::cerr << "cann't open " << filename << std::endl;
return;
}
std::string line;
while(std::getline(file, line)) {
if(line.empty()) {
continue;
}
std::string new_line;
std::string str;
bool ok = false;
for (auto ch: line) {
if(ch == '\t') continue;
if(ch != ' ') {
str += ch;
if(ok == false) ok = true;
}
else {
if(ok == true) {
ok = false;
new_line += str + ';';
str = "";
}
}
}
if(str.empty() == false) {
new_line += str;
}
//new_line += str;
//std::cout << new_line << std::endl;
size_t pos = new_line.find("//");
if(pos == std::string::npos) {
// size_t pos1 = new_line.rfind(';');
// if(pos1 != std::string::npos) {
// new_line = new_line.substr(0, pos1);
// }
//std::cout << new_line << std::endl;
inputs.emplace_back(new_line);
count++;
}
else {
if(pos > 0) {
new_line = new_line.substr(0, pos);
size_t pos1 = new_line.rfind(';');
if(pos1 != std::string::npos) {
new_line = new_line.substr(0, pos1);
}
//std::cout << new_line << std::endl;
inputs.emplace_back(new_line);
count++;
}
}
}
file.close();

}

void Parser::advance() {
if(hasMoreCommands()) curremt_command = inputs[++current_id];
}

bool Parser::hasMoreCommands() const {
return current_id < count - 1;
}

Parser::C_TYPE Parser::command_type() const {
size_t pos = curremt_command.find(';');
std::string str = curremt_command.substr(0, pos);
if(str == "add" || str == "sub" || str == "neg" || str == "eq" || str == "gt" || str == "lt" || str == "and" || str == "or" || str == "not") {
return C_ARITHMETIC;
}
else if(str == "pop") {
return C_POP;
}
else if(str == "push") {
return C_PUSH;
}
else if(str == "label") {
return C_LABEL;
}
else if(str == "goto") {
return C_GOTO;
}
else if(str == "if-goto") {
return C_IF;
}
else if(str == "function") {
return C_FUNCTION;
}
else if(str == "call") {
return C_CALL;
}
else if(str == "return") {
return C_RETRUN;
}
else {
std::cerr << "error! undefined command type" << str << std::endl;
return C_ERROR;
}
}

std::string Parser::arg1() {
if(command_type() == C_ARITHMETIC) {
return curremt_command;
}
else if(command_type() == C_RETRUN) {

}
else if(twoArgs.count(command_type())){
size_t pos1 = curremt_command.find(';');
size_t pos2 = curremt_command.rfind(';');
std::string str = curremt_command.substr(pos1 + 1, pos2 - pos1 - 1);
return str;
}
else if(oneArgs.count(command_type())) {
size_t pos1 = curremt_command.find(';');
std::string str = curremt_command.substr(pos1 + 1);
return str;
}
else {
std::cerr << "return undefined arg: " << curremt_command << std::endl;
}
}

std::string Parser::arg2() {
if(twoArgs.count(command_type())) {
size_t pos1 = curremt_command.rfind(';');
std::string str = curremt_command.substr(pos1 + 1);
return str;
}
else {
std::cerr << "return undefined arg: " << curremt_command << std::endl;
}
}

void Parser::reset() {
inputs.clear();
curremt_command = "";
current_id = -1;
count = 0;
}

CodeWriter 类

+------------------------------------+
| CodeWriter |
+------------------------------------+
| - current_command : std::string |
| - my_filename : fs::path |
| - file : std::ofstream |
| - staticLabel : std::string |
| - lib : std::map<std::string, std::string> |
| - eq_i : int |
| - gt_i : int |
| - lt_i : int |
| - addr_i : int |
+------------------------------------+
| + setFileName(filename : const fs::path&) : void |
| + write(filename : const fs::path&) : void |
| + writeArithmetic(command_type : const std::string&) : void |
| + writePushPop(command_type : const std::string&, segment : const std::string&, index : const std::string&) : void |
| + writeLabel(label : const std::string&) : void |
| + writeGoto(label : const std::string&) : void |
| + writeIf(label : const std::string&) : void |
| + writeCall(function_name : const std::string&, numArgs : const std::string&) : void |
| + writeReturn() : void |
| + writeFunction(function_name : const std::string&, numArgs : const std::string&) : void |
| + close() : void |
| - saveCaller(segement : const std::string&) : void |
| - bootStrap() : void |
+------------------------------------+

//
// Created by APP on 24-10-23.
//

#ifndef CODEWRITER_H
#define CODEWRITER_H

#include <filesystem>
#include <string>
#include <map>
#include <fstream>

namespace fs = std::filesystem;

class CodeWriter {
public:
void setFileName(const fs::path &filename);
void write(const fs::path &filename);
void writeArithmetic(const std::string &command_type);
void writePushPop(const std::string &command_type, const std::string &segment, const std::string &index);
void writeLabel(const std::string &label);
void writeGoto(const std::string &label);
void writeIf(const std::string &label);
void writeCall(const std::string &function_name, const std::string &numArgs);
void writeReturn();
void writeFunction(const std::string &function_name, const std::string &numArgs);
void close();
private:
std::string current_command;
fs::path my_filename;
std::ofstream file;
std::string staticLabel;
std::map<std::string, std::string> lib ={
{"local", "LCL"},
{"argument", "ARG"},
{"this", "THIS"},
{"that", "THAT"}
};
int eq_i = 0;
int gt_i = 0;
int lt_i = 0;
int addr_i = 0;

void saveCaller(const std::string &segement);
void bootStrap();
};



#endif //CODEWRITER_H

//
// Created by APP on 24-10-23.
//

#include "CodeWriter.h"
#include <iostream>
#include <string>

void CodeWriter::bootStrap() {
file.open(my_filename, std::ios::app);
file << "@256" << std::endl;
file << "D=A" << std::endl;
file << "@SP" << std::endl;
file << "M=D" << std::endl;

file << "@1" << std::endl;
file << "D=A" << std::endl;
file << "@LCL" << std::endl;
file << "M=D" << std::endl;
file << "@2" << std::endl;
file << "D=A" << std::endl;
file << "@ARG" << std::endl;
file << "M=D" << std::endl;
file << "@3" << std::endl;
file << "D=A" << std::endl;
file << "@THIS" << std::endl;
file << "M=D" << std::endl;
file << "@4" << std::endl;
file << "D=A" << std::endl;
file << "@THAT" << std::endl;
file << "M=D" << std::endl;

writeCall("Sys.init", "0");
file.close();
}

void CodeWriter::setFileName(const fs::path &filename) {
if(filename.has_extension()) {
std::string str = filename.generic_string();
size_t pos = str.rfind('.');
this -> my_filename = str.substr(0, pos);
my_filename += ".asm";
//std::cerr << "file name: " << my_filename << std::endl;
}
else {
this -> my_filename = filename;
my_filename += ".asm";
//std::cerr << "file name: " << my_filename << std::endl;
bootStrap();
}

}


void CodeWriter::write(const fs::path &filename) {
file.open(my_filename, std::ios::app);
staticLabel = filename.stem().generic_string();
std::cout << staticLabel << std::endl;
if(file.is_open() == false) {
std::cerr << "error! cann't find the file: " << my_filename << std::endl;
}
}

void CodeWriter::close() {
file.close();
}

void CodeWriter::writeArithmetic(const std::string &command_type) {
if(command_type == "add") {
//取出两个元素,进行加法运算,最后加入到栈中
file << "@SP" << std::endl;
file << "AM=M-1" << std::endl; //更新SP
file << "D=M" << std::endl; //栈顶元素
file << "A=A-1" << std::endl; //第二个元素
file << "M=D+M" << std::endl; //进行加法运算
}
else if(command_type == "sub") {
file << "@SP" << std::endl;
file << "AM=M-1" << std::endl;
file << "D=M" << std::endl;
file << "A=A-1" << std::endl;
file << "M=M-D" << std::endl;
}
else if(command_type == "neg") {
file << "@SP" << std::endl;
file << "A=M-1" << std::endl;
file << "M=-M" << std::endl;
}
else if(command_type == "eq") {
file << "@SP" << std::endl;
file << "AM=M-1" << std::endl;
file << "D=M" << std::endl;
file << "A=A-1" << std::endl;
file << "D=M-D" << std::endl;
file << "M=-1" << std::endl;
file << "@EQ_" + std::to_string(eq_i) << std::endl;
file << "D;JEQ" << std::endl;
file << "@SP" << std::endl;
file << "A=M-1" << std::endl;
file << "M=0" << std::endl;
file << "(EQ_" + std::to_string(eq_i) + ")" << std::endl;
++eq_i;
}
else if(command_type == "gt") {
file << "@SP" << std::endl;
file << "AM=M-1" << std::endl;
file << "D=M" << std::endl;
file << "A=A-1" << std::endl;
file << "D=M-D" << std::endl;
file << "M=-1" << std::endl;
file << "@GT_" + std::to_string(gt_i) << std::endl;
file << "D;JGT" << std::endl;
file << "@SP" << std::endl;
file << "A=M-1" << std::endl;
file << "M=0" << std::endl;
file << "(GT_" + std::to_string(gt_i) + ")" << std::endl;
++gt_i;
}
else if(command_type == "lt") {
file << "@SP" << std::endl;
file << "AM=M-1" << std::endl;
file << "D=M" << std::endl;
file << "A=A-1" << std::endl;
file << "D=M-D" << std::endl;
file << "M=-1" << std::endl;
file << "@LT_" + std::to_string(lt_i) << std::endl;
file << "D;JLT" << std::endl;
file << "@SP" << std::endl;
file << "A=M-1" << std::endl;
file << "M=0" << std::endl;
file << "(LT_" + std::to_string(lt_i) + ")" << std::endl;
++lt_i;
}
else if(command_type == "and") {
file << "@SP" << std::endl;
file << "AM=M-1" << std::endl;
file << "D=M" << std::endl;
file << "A=A-1" << std::endl;
file << "M=D&M" << std::endl;
}
else if(command_type == "or") {
file << "@SP" << std::endl;
file << "AM=M-1" << std::endl;
file << "D=M" << std::endl;
file << "A=A-1" << std::endl;
file << "M=D|M" << std::endl;
}
else if(command_type == "not") {
file << "@SP" << std::endl;
file << "A=M-1" << std::endl;
file << "M=!M" << std::endl;
}
}

void CodeWriter::writePushPop(const std::string &command_type, const std::string &segment, const std::string &index) {
if(command_type == "push") {
if(segment == "constant") {
file << "@" + index << std::endl;
file << "D=A" << std::endl;
}
else if(lib.count(segment)) {
file << "@" + index << std::endl;
file << "D=A" << std::endl;
file << "@" + lib[segment] << std::endl;
file << "A=D+M" << std::endl;
file << "D=M" << std::endl;
}
else if(segment == "pointer") {
file << "@" + index << std::endl;
file << "D=A" << std::endl;
file << "@3" << std::endl;
file << "A=D+A" << std::endl;
file << "D=M" << std::endl;
}
else if(segment == "temp") {
file << "@" + index << std::endl;
file << "D=A" << std::endl;
file << "@5" << std::endl;
file << "A=D+A" << std::endl;
file << "D=M" << std::endl;
}
else if(segment == "static") {
file << "@" + staticLabel + "." + index << std::endl;
file << "D=M" << std::endl;
}
else if(segment == "address"){
file << "@RA_" + index << std::endl;
file << "D=A" << std::endl;
}
//加入栈中,更新SP
file << "@SP" << std::endl;
file << "A=M" << std::endl;
file << "M=D" << std::endl;
file << "@SP" << std::endl;
file << "M=M+1" << std::endl;
}
else if(command_type == "pop") {
if(lib.count(segment)) {
file << "@" + index << std::endl;
file << "D=A" << std::endl;
file << "@" + lib[segment] << std::endl;
file << "D=D+M" << std::endl;
}
else if(segment == "pointer") {
file << "@" + index << std::endl;
file << "D=A" << std::endl;
file << "@3" << std::endl;
file << "D=D+A" << std::endl;
}
else if(segment == "temp") {
file << "@" + index << std::endl;
file << "D=A" << std::endl;
file << "@5" << std::endl;
file << "D=D+A" << std::endl;
}
else if(segment == "static") {
file << "@" + staticLabel + "." + index << std::endl;
file << "D=A" << std::endl;
}
//弹出栈,更新SP,需要多使用一个寄存器,不然无法实现
file << "@R13" << std::endl;
file << "M=D" << std::endl;
file << "@SP" << std::endl;
file << "AM=M-1" << std::endl;
file << "D=M" << std::endl;
file << "@R13" << std::endl;
file << "A=M" << std::endl;
file << "M=D" << std::endl;
}
}

void CodeWriter::writeGoto(const std::string &label) {
file << "//" + label << std::endl;
file << "@" + label << std::endl;
file << "0;JMP" << std::endl;
}

void CodeWriter::writeIf(const std::string &label) {
file << "//if-goto " + label << std::endl;
file << "@SP" << std::endl;
file << "AM=M-1" << std::endl;
file << "D=M" << std::endl;
file << "@" + label << std::endl;
file << "D;JNE" << std::endl;
}

void CodeWriter::writeLabel(const std::string &label) {
file << "//" + label << std::endl;
file << "(" + label + ")" << std::endl;
}

void CodeWriter::writeReturn() {
//FRAME=LCL,FRAME是临时变量:这里使用R13进行表示
file << "@LCL" << std::endl;
file << "D=M" << std::endl;
file << "@R13" << std::endl;
file << "M=D" << std::endl;

//RET=*(FRAME-5),RET是临时变量,这里使用R14进行表示
file << "@5" << std::endl;
file << "D=A" << std::endl;
file << "@R13" << std::endl;
file << "A=M-D" << std::endl;
file << "D=M" << std::endl;
file << "@R14" << std::endl;
file << "M=D" << std::endl;

//或写作
// file << "@5" << std::endl;
// file << "A=D-A" << std::endl;
// file << "D=M" << std::endl;
// file << "@R14" << std::endl;
// file << "M=D" << std::endl;

file << "@SP" << std::endl;
file << "AM=M-1" << std::endl;
file << "D=M" << std::endl;
file << "@ARG" << std::endl;
file << "A=M" << std::endl;
file << "M=D" << std::endl;

file << "@ARG" << std::endl;
file << "D=M" << std::endl;
file << "@SP" << std::endl;
file << "M=D+1" << std::endl;

file << "@R13" << std::endl;
file << "A=M-1" << std::endl;
file << "D=M" << std::endl;
file << "@THAT" << std::endl;
file << "M=D" << std::endl;

file << "@2" << std::endl;
file << "D=A" << std::endl;
file << "@R13" << std::endl;
file << "A=M-D" << std::endl;
file << "D=M" << std::endl;
file << "@THIS" << std::endl;
file << "M=D" << std::endl;

file << "@3" << std::endl;
file << "D=A" << std::endl;
file << "@R13" << std::endl;
file << "A=M-D" << std::endl;
file << "D=M" << std::endl;
file << "@ARG" << std::endl;
file << "M=D" << std::endl;

file << "@4" << std::endl;
file << "D=A" << std::endl;
file << "@R13" << std::endl;
file << "A=M-D" << std::endl;
file << "D=M" << std::endl;
file << "@LCL" << std::endl;
file << "M=D" << std::endl;

file << "@R14" << std::endl;
file << "A=M" << std::endl;
file << "0;JMP" << std::endl;
}

void CodeWriter::writeCall(const std::string &function_name, const std::string &numArgs) {
file << "//writeCall: " + function_name + ": " +numArgs << std::endl;
writePushPop("push", "address", std::to_string(addr_i));
saveCaller("LCL");
saveCaller("ARG");
saveCaller("THIS");
saveCaller("THAT");

//ARG=SP-n-5
file << "//ARG=SP-" + numArgs +"-5" << std::endl;
file << "@" + numArgs << std::endl;
file << "D=A" << std::endl;
file << "@5" << std::endl;
file << "D=D+A" << std::endl;
file << "@SP" << std::endl;
file << "D=M-D" << std::endl;
file << "@ARG" << std::endl;
file << "M=D" << std::endl;

//LCL=SP
file << "//LCL=SP" << std::endl;
file << "@SP" << std::endl;
file << "D=M" << std::endl;
file << "@LCL" << std::endl;
file << "M=D" << std::endl;

//goto f
//(return-address)
file << "//goto " + function_name << std::endl;
file << "//return_address: " + std::to_string(addr_i) << std::endl;
writeGoto(function_name);
writeLabel("RA_" + std::to_string(addr_i));
addr_i++;
}

void CodeWriter::writeFunction(const std::string &function_name, const std::string &numArgs) {
file << "//function " + function_name + ": " + numArgs << std::endl;
file << "(" + function_name + ")" << std::endl;
int nArgs = std::stoi(numArgs);
file << "// Push nArgs: initialize 0" << std::endl;
for (int i = 0; i < nArgs; ++i) {
file << "@SP" << std::endl;
file << "A=M" << std::endl;
file << "M=0" << std::endl;
file << "@SP" << std::endl;
file << "M=M+1" << std::endl;
}
}

void CodeWriter::saveCaller(const std::string &segement) {
file << "@" + segement << std::endl;
file << "D=M" << std::endl;
file << "@SP" << std::endl;
file << "A=M" << std::endl;
file << "M=D" << std::endl;
file << "@SP" << std::endl;
file << "M=M+1" << std::endl;
}

VMtranslater 类

+-------------------------------+
| VMtranslater |
+-------------------------------+
| - parser : Parser |
| - code_writer : CodeWriter |
+-------------------------------+
| + VMtranslater() |
| + init(filename : const fs::path&) : void |
| + convert(filename : const fs::path&) : void |
+-------------------------------+

//
// Created by APP on 24-10-23.
//

#ifndef VMTRANSLATER_H
#define VMTRANSLATER_H

#include "Parser.h"
#include "CodeWriter.h"
#include <filesystem>

namespace fs = std::filesystem;

class VMtranslater {
public:
VMtranslater() = default;
void init(const fs::path &filename);
void convert(const fs::path &filename);
private:
Parser parser;
CodeWriter code_writer;
};



#endif //VMTRANSLATER_H

//
// Created by APP on 24-10-23.
//

#include "VMtranslater.h"
#include <iostream>

void VMtranslater::init(const fs::path &filename) {
if(fs::exists(filename)) {
if(fs::is_regular_file(filename)) {
if(filename.extension() == ".vm") {
code_writer.setFileName(filename);
convert(filename);
std::cout << "finish file" << std::endl;
}
}
else if(fs::is_directory(filename)) {
code_writer.setFileName(filename);
int count = 0;
for (const auto& entry: fs::directory_iterator(filename)) {
if(entry.path().extension() == ".vm") {
convert(entry.path());
std::cout << "finish Dictionary: file " << ++count << std::endl;
}
}
std::cout << "finish all files: " << count << std::endl;
}
}
else {
std::cerr << "error! cann't find '.vm' file" << std::endl;
}
}


void VMtranslater::convert(const fs::path &filename) {
parser.reset();
parser.init(filename);
code_writer.write(filename);

while(parser.hasMoreCommands()) {
parser.advance();
if(parser.command_type() == Parser::C_ARITHMETIC) {
std::string arg = parser.arg1();
code_writer.writeArithmetic(arg);
}
else if(parser.command_type() == Parser::C_POP) {
std::string arg1 = parser.arg1();
std::string arg2 = parser.arg2();
code_writer.writePushPop("pop", arg1, arg2);
}
else if(parser.command_type() == Parser::C_PUSH) {
std::string arg1 = parser.arg1();
std::string arg2 = parser.arg2();
code_writer.writePushPop("push", arg1, arg2);
}
else if(parser.command_type() == Parser::C_IF) {
std::string label = parser.arg1();
code_writer.writeIf(label);
}
else if(parser.command_type() == Parser::C_LABEL) {
std::string label = parser.arg1();
code_writer.writeLabel(label);
}
else if(parser.command_type() == Parser::C_GOTO) {
std::string label = parser.arg1();
code_writer.writeGoto(label);
}
else if(parser.command_type() == Parser::C_FUNCTION) {
std::string arg1 = parser.arg1();
std::string arg2 = parser.arg2();
code_writer.writeFunction(arg1,arg2);
}
else if(parser.command_type() == Parser::C_CALL) {
std::string arg1 = parser.arg1();
std::string arg2 = parser.arg2();
code_writer.writeCall(arg1,arg2);
}
else if(parser.command_type() == Parser::C_RETRUN) {
code_writer.writeReturn();
}
}
code_writer.close();
}

main

#include <iostream>
#include "VMtranslater.h"
namespace fs = std::filesystem;
int main()
{
VMtranslater my_translater;
fs::path input_path("../AllTest/FunctionCalls/StaticsTest");
my_translater.init(input_path);
return 0;
}

剩余的两个部分是编译器和操作系统,寒假再搞。