From b44b7d0aadc8768fefd8bac694a591ea2e11e064 Mon Sep 17 00:00:00 2001 From: evilolipop Date: Mon, 5 Feb 2024 15:50:34 +0800 Subject: [PATCH] feat(vmtranslator): add stuff --- vmtranslator/code_writer.go | 209 +++++++- vmtranslator/main.go | 81 ++- .../FibonacciElement/FibonacciElement.asm | 386 ++++++++++++++ .../FunctionCalls/FibonacciElement/Main.vm | 30 ++ .../FunctionCalls/FibonacciElement/Sys.vm | 16 + .../FunctionCalls/NestedCall/NestedCall.asm | 495 ++++++++++++++++++ .../testdata/FunctionCalls/NestedCall/Sys.vm | 57 ++ .../SimpleFunction/SimpleFunction.asm | 115 ++++ .../SimpleFunction/SimpleFunction.vm | 17 + .../FunctionCalls/StaticsTest/Class1.vm | 20 + .../FunctionCalls/StaticsTest/Class2.vm | 20 + .../FunctionCalls/StaticsTest/StaticsTest.asm | 236 +++++++++ .../testdata/FunctionCalls/StaticsTest/Sys.vm | 20 + .../ProgramFlow/BasicLoop/BasicLoop.asm | 116 ++++ .../ProgramFlow/BasicLoop/BasicLoop.vm | 23 + .../FibonacciSeries/FibonacciSeries.asm | 214 ++++++++ .../FibonacciSeries/FibonacciSeries.vm | 44 ++ .../StackArithmetic/StackTest/StackTest.asm | 4 +- 18 files changed, 2066 insertions(+), 37 deletions(-) create mode 100644 vmtranslator/testdata/FunctionCalls/FibonacciElement/FibonacciElement.asm create mode 100644 vmtranslator/testdata/FunctionCalls/FibonacciElement/Main.vm create mode 100644 vmtranslator/testdata/FunctionCalls/FibonacciElement/Sys.vm create mode 100644 vmtranslator/testdata/FunctionCalls/NestedCall/NestedCall.asm create mode 100644 vmtranslator/testdata/FunctionCalls/NestedCall/Sys.vm create mode 100644 vmtranslator/testdata/FunctionCalls/SimpleFunction/SimpleFunction.asm create mode 100644 vmtranslator/testdata/FunctionCalls/SimpleFunction/SimpleFunction.vm create mode 100644 vmtranslator/testdata/FunctionCalls/StaticsTest/Class1.vm create mode 100644 vmtranslator/testdata/FunctionCalls/StaticsTest/Class2.vm create mode 100644 vmtranslator/testdata/FunctionCalls/StaticsTest/StaticsTest.asm create mode 100644 vmtranslator/testdata/FunctionCalls/StaticsTest/Sys.vm create mode 100644 vmtranslator/testdata/ProgramFlow/BasicLoop/BasicLoop.asm create mode 100644 vmtranslator/testdata/ProgramFlow/BasicLoop/BasicLoop.vm create mode 100644 vmtranslator/testdata/ProgramFlow/FibonacciSeries/FibonacciSeries.asm create mode 100644 vmtranslator/testdata/ProgramFlow/FibonacciSeries/FibonacciSeries.vm diff --git a/vmtranslator/code_writer.go b/vmtranslator/code_writer.go index 690bec0..3238427 100644 --- a/vmtranslator/code_writer.go +++ b/vmtranslator/code_writer.go @@ -2,15 +2,22 @@ package main import ( "bytes" + "errors" "fmt" "io" + "io/fs" + "regexp" "strconv" ) +var validLabel = `^[A-Za-z_:.][\w_:.]*$` + type CodeWriter struct { fileName string w io.WriteCloser jump int + retIndex int + function string } func NewCodeWriter(w io.WriteCloser) *CodeWriter { @@ -19,7 +26,7 @@ func NewCodeWriter(w io.WriteCloser) *CodeWriter { } } -func (c *CodeWriter) SetFileNmae(fileName string) { +func (c *CodeWriter) SetFileName(fileName string) { c.fileName = fileName } @@ -28,13 +35,14 @@ func (c *CodeWriter) WriteArithmetic(command string) { switch command { case "add": - popBinary(&buffer) + popUnaryAndGetTop(&buffer) buffer.WriteString("M=M+D\n") case "sub": - popBinary(&buffer) + popUnaryAndGetTop(&buffer) buffer.WriteString("M=M-D\n") case "neg": - popUnary(&buffer) + buffer.WriteString("@SP\n") + buffer.WriteString("A=M-1\n") buffer.WriteString("M=-M\n") case "eq": c.jump = writeCompare(&buffer, c.jump, "JEQ") @@ -43,13 +51,14 @@ func (c *CodeWriter) WriteArithmetic(command string) { case "lt": c.jump = writeCompare(&buffer, c.jump, "JLT") case "and": - popBinary(&buffer) + popUnaryAndGetTop(&buffer) buffer.WriteString("M=M&D\n") case "or": - popBinary(&buffer) + popUnaryAndGetTop(&buffer) buffer.WriteString("M=M|D\n") case "not": - popUnary(&buffer) + buffer.WriteString("@SP\n") + buffer.WriteString("A=M-1\n") buffer.WriteString("M=!M\n") } @@ -137,7 +146,6 @@ func (c *CodeWriter) WritePushPop(command CommandType, segment string, index int popUnary(&buffer) - buffer.WriteString("D=M\n") buffer.WriteString("@R13\n") buffer.WriteString("A=M\n") buffer.WriteString("M=D\n") @@ -151,10 +159,180 @@ func (c *CodeWriter) WritePushPop(command CommandType, segment string, index int } } +func (c *CodeWriter) WriteInit() { + buffer := bytes.Buffer{} + buffer.WriteString("@256\n") + buffer.WriteString("D=A\n") + buffer.WriteString("@SP\n") + buffer.WriteString("M=D\n") + _, err := c.w.Write(buffer.Bytes()) + if err != nil { + panic(err) + } + + c.WriteCall("Sys.init", 0) +} + +func (c *CodeWriter) WriteLabel(label string) { + reg := regexp.MustCompile(validLabel) + if !reg.MatchString(label) { + panic("invalid label: " + label) + } + + buffer := bytes.Buffer{} + buffer.WriteString("(" + c.function + "$" + label + ")\n") + _, err := c.w.Write(buffer.Bytes()) + if err != nil { + panic(err) + } +} + +func (c *CodeWriter) WriteGoto(label string) { + buffer := bytes.Buffer{} + buffer.WriteString("@" + c.function + "$" + label + "\n") + buffer.WriteString("0;JMP\n") + _, err := c.w.Write(buffer.Bytes()) + if err != nil { + panic(err) + } +} + +func (c *CodeWriter) WriteIf(label string) { + buffer := bytes.Buffer{} + popUnary(&buffer) + buffer.WriteString("@" + c.function + "$" + label + "\n") + buffer.WriteString("D;JNE\n") + _, err := c.w.Write(buffer.Bytes()) + if err != nil { + panic(err) + } +} + +func (c *CodeWriter) WriteCall(functionName string, numArgs int) { + buffer := bytes.Buffer{} + returnAddress := "_" + functionName + "_RETURN_" + strconv.Itoa(c.retIndex) + c.retIndex++ + // push return address + buffer.WriteString("@" + returnAddress + "\n") + buffer.WriteString("D=A\n") + push(&buffer) + // push LCL, ARG, THIS, THAT + buffer.WriteString("@LCL\n") + buffer.WriteString("D=M\n") + push(&buffer) + buffer.WriteString("@ARG\n") + buffer.WriteString("D=M\n") + push(&buffer) + buffer.WriteString("@THIS\n") + buffer.WriteString("D=M\n") + push(&buffer) + buffer.WriteString("@THAT\n") + buffer.WriteString("D=M\n") + push(&buffer) + // ARG = SP - (n + 5) + buffer.WriteString("@" + strconv.Itoa(numArgs+5) + "\n") + buffer.WriteString("D=D-A\n") + buffer.WriteString("@ARG\n") + buffer.WriteString("M=D\n") + // LCL = SP + buffer.WriteString("@SP\n") + buffer.WriteString("D=M\n") + buffer.WriteString("@LCL\n") + buffer.WriteString("M=D\n") + // goto function + buffer.WriteString("@" + functionName + "\n") + buffer.WriteString("0;JMP\n") + // return address + buffer.WriteString("(" + returnAddress + ")\n") + _, err := c.w.Write(buffer.Bytes()) + if err != nil { + panic(err) + } +} + +func (c *CodeWriter) WriteReturn() { + buffer := bytes.Buffer{} + // FRAME = LCL + buffer.WriteString("@LCL\n") + buffer.WriteString("D=M\n") + buffer.WriteString("@R13\n") + buffer.WriteString("M=D\n") + // RET = *(FRAME - 5) + // if arg num is 0, pop() will overwrite RET, so we need to save it + buffer.WriteString("@5\n") + buffer.WriteString("A=D-A\n") + buffer.WriteString("D=M\n") + buffer.WriteString("@R14\n") + buffer.WriteString("M=D\n") + // *ARG = pop() + popUnary(&buffer) + buffer.WriteString("@ARG\n") + buffer.WriteString("A=M\n") + buffer.WriteString("M=D\n") + // SP = ARG + 1 + buffer.WriteString("@ARG\n") + buffer.WriteString("D=M+1\n") + buffer.WriteString("@SP\n") + buffer.WriteString("M=D\n") + // THAT = *(FRAME - 1) + buffer.WriteString("@R13\n") + buffer.WriteString("AM=M-1\n") + buffer.WriteString("D=M\n") + buffer.WriteString("@THAT\n") + buffer.WriteString("M=D\n") + // THIS = *(FRAME - 2) + buffer.WriteString("@R13\n") + buffer.WriteString("AM=M-1\n") + buffer.WriteString("D=M\n") + buffer.WriteString("@THIS\n") + buffer.WriteString("M=D\n") + // ARG = *(FRAME - 3) + buffer.WriteString("@R13\n") + buffer.WriteString("AM=M-1\n") + buffer.WriteString("D=M\n") + buffer.WriteString("@ARG\n") + buffer.WriteString("M=D\n") + // LCL = *(FRAME - 4) + buffer.WriteString("@R13\n") + buffer.WriteString("AM=M-1\n") + buffer.WriteString("D=M\n") + buffer.WriteString("@LCL\n") + buffer.WriteString("M=D\n") + // goto RET + buffer.WriteString("@R14\n") + buffer.WriteString("A=M\n") + buffer.WriteString("0;JMP\n") + _, err := c.w.Write(buffer.Bytes()) + if err != nil { + panic(err) + } +} + +func (c *CodeWriter) WriteFunction(functionName string, numLocals int) { + reg := regexp.MustCompile(validLabel) + if !reg.MatchString(functionName) { + panic("invalid function name: " + functionName) + } + + buffer := bytes.Buffer{} + c.function = functionName + buffer.WriteString("(" + functionName + ")\n") + _, err := c.w.Write(buffer.Bytes()) + if err != nil { + panic(err) + } + + for i := 0; i < numLocals; i++ { + c.WritePushPop(C_PUSH, "constant", 0) + } +} + func (c *CodeWriter) Close() { err := c.w.Close() if err != nil { - panic(err) + if !errors.Is(err, fs.ErrClosed) { + panic(err) + } } } @@ -171,7 +349,7 @@ func writeTrue(buf *bytes.Buffer) { } func writeCompare(buffer *bytes.Buffer, jump int, compare string) int { - popBinary(buffer) + popUnaryAndGetTop(buffer) buffer.WriteString("D=M-D\n") buffer.WriteString("@JUMP_" + strconv.Itoa(jump) + "\n") buffer.WriteString("D;" + compare + "\n") @@ -192,18 +370,17 @@ func writeOffset(buf *bytes.Buffer, index int) { buf.WriteString("D=M\n") } -// one value in D, one value in M -func popBinary(buf *bytes.Buffer) { - buf.WriteString("@SP\n") - buf.WriteString("AM=M-1\n") - buf.WriteString("D=M\n") +// pop value and put it in D, then point to the last value +func popUnaryAndGetTop(buf *bytes.Buffer) { + popUnary(buf) buf.WriteString("A=A-1\n") } -// one value in M +// pop value and put it in D func popUnary(buf *bytes.Buffer) { buf.WriteString("@SP\n") buf.WriteString("AM=M-1\n") + buf.WriteString("D=M\n") } func push(buf *bytes.Buffer) { diff --git a/vmtranslator/main.go b/vmtranslator/main.go index c5639a1..6e4a90c 100644 --- a/vmtranslator/main.go +++ b/vmtranslator/main.go @@ -15,35 +15,66 @@ func main() { } arg := os.Args[1] - err := filepath.WalkDir(arg, func(path string, d os.DirEntry, err error) error { + + if strings.HasSuffix(arg, ".vm") { + out, err := os.Create(strings.TrimSuffix(filepath.Base(arg), ".vm") + ".asm") + if err != nil { + panic(err) + } + + codeWriter := NewCodeWriter(out) + defer codeWriter.Close() + codeWriter.WriteInit() + + in, err := os.Open(arg) if err != nil { - return err + panic(err) } - if strings.HasSuffix(path, ".vm") { - in, err := os.Open(path) + parse(in, codeWriter, arg) + } else { + vmFound := false + var codeWriter *CodeWriter + + err := filepath.WalkDir(arg, func(path string, d os.DirEntry, err error) error { if err != nil { return err } - parse(in, path) - } - return nil - }) - if err != nil { - panic(err) - } -} + if strings.HasSuffix(path, ".vm") { + if !vmFound { + dir := filepath.Dir(path) + out, err := os.Create(dir + string(filepath.Separator) + filepath.Base(dir) + ".asm") + if err != nil { + panic(err) + } + codeWriter = NewCodeWriter(out) + codeWriter.WriteInit() + vmFound = true + } -func parse(in io.Reader, path string) { - out, err := os.Create(path[:len(path)-3] + ".asm") - if err != nil { - panic(err) + in, err := os.Open(path) + if err != nil { + return err + } + parse(in, codeWriter, path) + } else { + if codeWriter != nil { + codeWriter.Close() + } + vmFound = false + } + + return nil + }) + if err != nil { + panic(err) + } } +} - codeWriter := NewCodeWriter(out) - codeWriter.SetFileNmae(strings.TrimSuffix(filepath.Base(path), ".vm")) - defer codeWriter.w.Close() +func parse(in io.Reader, codeWriter *CodeWriter, path string) { + codeWriter.SetFileName(strings.TrimSuffix(filepath.Base(path), ".vm")) parser, err := NewParser(in) if err != nil { @@ -59,6 +90,18 @@ func parse(in io.Reader, path string) { codeWriter.WriteArithmetic(parser.Arg1()) case C_PUSH, C_POP: codeWriter.WritePushPop(cmdType, parser.Arg1(), parser.Arg2()) + case C_LABEL: + codeWriter.WriteLabel(parser.Arg1()) + case C_GOTO: + codeWriter.WriteGoto(parser.Arg1()) + case C_IF: + codeWriter.WriteIf(parser.Arg1()) + case C_FUNCTION: + codeWriter.WriteFunction(parser.Arg1(), parser.Arg2()) + case C_RETURN: + codeWriter.WriteReturn() + case C_CALL: + codeWriter.WriteCall(parser.Arg1(), parser.Arg2()) default: panic(fmt.Sprintf("unknown command type: %v", cmdType)) } diff --git a/vmtranslator/testdata/FunctionCalls/FibonacciElement/FibonacciElement.asm b/vmtranslator/testdata/FunctionCalls/FibonacciElement/FibonacciElement.asm new file mode 100644 index 0000000..17498c2 --- /dev/null +++ b/vmtranslator/testdata/FunctionCalls/FibonacciElement/FibonacciElement.asm @@ -0,0 +1,386 @@ +@256 +D=A +@SP +M=D +@_Sys.init_RETURN_0 +D=A +@SP +A=M +M=D +@SP +M=M+1 +@LCL +D=M +@SP +A=M +M=D +@SP +M=M+1 +@ARG +D=M +@SP +A=M +M=D +@SP +M=M+1 +@THIS +D=M +@SP +A=M +M=D +@SP +M=M+1 +@THAT +D=M +@SP +A=M +M=D +@SP +M=M+1 +@5 +D=D-A +@ARG +M=D +@SP +D=M +@LCL +M=D +@Sys.init +0;JMP +(_Sys.init_RETURN_0) +(Main.fibonacci) +@ARG +D=M +@0 +A=D+A +D=M +@SP +A=M +M=D +@SP +M=M+1 +@2 +D=A +@SP +A=M +M=D +@SP +M=M+1 +@SP +AM=M-1 +D=M +A=A-1 +D=M-D +@JUMP_0 +D;JLT +@SP +A=M-1 +M=0 +@END_0 +0;JMP +(JUMP_0) +@SP +A=M-1 +M=-1 +(END_0) +@SP +AM=M-1 +D=M +@Main.fibonacci$N_LT_2 +D;JNE +@Main.fibonacci$N_GE_2 +0;JMP +(Main.fibonacci$N_LT_2) +@ARG +D=M +@0 +A=D+A +D=M +@SP +A=M +M=D +@SP +M=M+1 +@LCL +D=M +@R13 +M=D +@5 +A=D-A +D=M +@R14 +M=D +@SP +AM=M-1 +D=M +@ARG +A=M +M=D +@ARG +D=M+1 +@SP +M=D +@R13 +AM=M-1 +D=M +@THAT +M=D +@R13 +AM=M-1 +D=M +@THIS +M=D +@R13 +AM=M-1 +D=M +@ARG +M=D +@R13 +AM=M-1 +D=M +@LCL +M=D +@R14 +A=M +0;JMP +(Main.fibonacci$N_GE_2) +@ARG +D=M +@0 +A=D+A +D=M +@SP +A=M +M=D +@SP +M=M+1 +@2 +D=A +@SP +A=M +M=D +@SP +M=M+1 +@SP +AM=M-1 +D=M +A=A-1 +M=M-D +@_Main.fibonacci_RETURN_1 +D=A +@SP +A=M +M=D +@SP +M=M+1 +@LCL +D=M +@SP +A=M +M=D +@SP +M=M+1 +@ARG +D=M +@SP +A=M +M=D +@SP +M=M+1 +@THIS +D=M +@SP +A=M +M=D +@SP +M=M+1 +@THAT +D=M +@SP +A=M +M=D +@SP +M=M+1 +@6 +D=D-A +@ARG +M=D +@SP +D=M +@LCL +M=D +@Main.fibonacci +0;JMP +(_Main.fibonacci_RETURN_1) +@ARG +D=M +@0 +A=D+A +D=M +@SP +A=M +M=D +@SP +M=M+1 +@1 +D=A +@SP +A=M +M=D +@SP +M=M+1 +@SP +AM=M-1 +D=M +A=A-1 +M=M-D +@_Main.fibonacci_RETURN_2 +D=A +@SP +A=M +M=D +@SP +M=M+1 +@LCL +D=M +@SP +A=M +M=D +@SP +M=M+1 +@ARG +D=M +@SP +A=M +M=D +@SP +M=M+1 +@THIS +D=M +@SP +A=M +M=D +@SP +M=M+1 +@THAT +D=M +@SP +A=M +M=D +@SP +M=M+1 +@6 +D=D-A +@ARG +M=D +@SP +D=M +@LCL +M=D +@Main.fibonacci +0;JMP +(_Main.fibonacci_RETURN_2) +@SP +AM=M-1 +D=M +A=A-1 +M=M+D +@LCL +D=M +@R13 +M=D +@5 +A=D-A +D=M +@R14 +M=D +@SP +AM=M-1 +D=M +@ARG +A=M +M=D +@ARG +D=M+1 +@SP +M=D +@R13 +AM=M-1 +D=M +@THAT +M=D +@R13 +AM=M-1 +D=M +@THIS +M=D +@R13 +AM=M-1 +D=M +@ARG +M=D +@R13 +AM=M-1 +D=M +@LCL +M=D +@R14 +A=M +0;JMP +(Sys.init) +@4 +D=A +@SP +A=M +M=D +@SP +M=M+1 +@_Main.fibonacci_RETURN_3 +D=A +@SP +A=M +M=D +@SP +M=M+1 +@LCL +D=M +@SP +A=M +M=D +@SP +M=M+1 +@ARG +D=M +@SP +A=M +M=D +@SP +M=M+1 +@THIS +D=M +@SP +A=M +M=D +@SP +M=M+1 +@THAT +D=M +@SP +A=M +M=D +@SP +M=M+1 +@6 +D=D-A +@ARG +M=D +@SP +D=M +@LCL +M=D +@Main.fibonacci +0;JMP +(_Main.fibonacci_RETURN_3) +(Sys.init$END) +@Sys.init$END +0;JMP diff --git a/vmtranslator/testdata/FunctionCalls/FibonacciElement/Main.vm b/vmtranslator/testdata/FunctionCalls/FibonacciElement/Main.vm new file mode 100644 index 0000000..84d9f4d --- /dev/null +++ b/vmtranslator/testdata/FunctionCalls/FibonacciElement/Main.vm @@ -0,0 +1,30 @@ +// 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/08/FunctionCalls/FibonacciElement/Main.vm + +// Computes the n'th element of the Fibonacci series, recursively. +// n is given in argument[0]. Called by the Sys.init function +// (part of the Sys.vm file), which sets argument[0] to an input +// value and then calls Main.fibonacci. + +function Main.fibonacci 0 + push argument 0 + push constant 2 + lt + if-goto N_LT_2 + goto N_GE_2 +label N_LT_2 // if n < 2 returns n + push argument 0 + return +label N_GE_2 // if n >= 2 returns fib(n - 2) + fib(n - 1) + push argument 0 + push constant 2 + sub + call Main.fibonacci 1 // computes fib(n - 2) + push argument 0 + push constant 1 + sub + call Main.fibonacci 1 // computes fib(n - 1) + add // returns fib(n - 1) + fib(n - 2) + return diff --git a/vmtranslator/testdata/FunctionCalls/FibonacciElement/Sys.vm b/vmtranslator/testdata/FunctionCalls/FibonacciElement/Sys.vm new file mode 100644 index 0000000..391907b --- /dev/null +++ b/vmtranslator/testdata/FunctionCalls/FibonacciElement/Sys.vm @@ -0,0 +1,16 @@ +// 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/08/FunctionCalls/FibonacciElement/Sys.vm + +// This Sys.vm file containts one function: Sys.init. + +// Pushes a constant, say n, onto the stack, and calls the Main.fibonacii +// function, which computes the n'th element of the Fibonacci series. +// Note that by convention, the Sys.init function is called "automatically" +// by the bootstrap code written by the VM translator. +function Sys.init 0 + push constant 4 + call Main.fibonacci 1 // computes the 4'th fibonacci element +label END + goto END // loops infinitely diff --git a/vmtranslator/testdata/FunctionCalls/NestedCall/NestedCall.asm b/vmtranslator/testdata/FunctionCalls/NestedCall/NestedCall.asm new file mode 100644 index 0000000..fef5507 --- /dev/null +++ b/vmtranslator/testdata/FunctionCalls/NestedCall/NestedCall.asm @@ -0,0 +1,495 @@ +(Sys.init) +@4000 +D=A +@SP +A=M +M=D +@SP +M=M+1 +@THIS +D=A +@R13 +M=D +@SP +AM=M-1 +D=M +@R13 +A=M +M=D +@5000 +D=A +@SP +A=M +M=D +@SP +M=M+1 +@THAT +D=A +@R13 +M=D +@SP +AM=M-1 +D=M +@R13 +A=M +M=D +@_Sys.main_RETURN_0 +D=A +@SP +A=M +M=D +@SP +M=M+1 +@LCL +D=M +@SP +A=M +M=D +@SP +M=M+1 +@ARG +D=M +@SP +A=M +M=D +@SP +M=M+1 +@THIS +D=M +@SP +A=M +M=D +@SP +M=M+1 +@THAT +D=M +@SP +A=M +M=D +@SP +M=M+1 +@5 +D=D-A +@ARG +M=D +@SP +D=M +@LCL +M=D +@Sys.main +0;JMP +(_Sys.main_RETURN_0) +@R6 +D=A +@R13 +M=D +@SP +AM=M-1 +D=M +@R13 +A=M +M=D +(Sys.init$LOOP) +@Sys.init$LOOP +0;JMP +(Sys.main) +@0 +D=A +@SP +A=M +M=D +@SP +M=M+1 +@0 +D=A +@SP +A=M +M=D +@SP +M=M+1 +@0 +D=A +@SP +A=M +M=D +@SP +M=M+1 +@0 +D=A +@SP +A=M +M=D +@SP +M=M+1 +@0 +D=A +@SP +A=M +M=D +@SP +M=M+1 +@4001 +D=A +@SP +A=M +M=D +@SP +M=M+1 +@THIS +D=A +@R13 +M=D +@SP +AM=M-1 +D=M +@R13 +A=M +M=D +@5001 +D=A +@SP +A=M +M=D +@SP +M=M+1 +@THAT +D=A +@R13 +M=D +@SP +AM=M-1 +D=M +@R13 +A=M +M=D +@200 +D=A +@SP +A=M +M=D +@SP +M=M+1 +@LCL +D=M +@1 +D=D+A +@R13 +M=D +@SP +AM=M-1 +D=M +@R13 +A=M +M=D +@40 +D=A +@SP +A=M +M=D +@SP +M=M+1 +@LCL +D=M +@2 +D=D+A +@R13 +M=D +@SP +AM=M-1 +D=M +@R13 +A=M +M=D +@6 +D=A +@SP +A=M +M=D +@SP +M=M+1 +@LCL +D=M +@3 +D=D+A +@R13 +M=D +@SP +AM=M-1 +D=M +@R13 +A=M +M=D +@123 +D=A +@SP +A=M +M=D +@SP +M=M+1 +@_Sys.add12_RETURN_1 +D=A +@SP +A=M +M=D +@SP +M=M+1 +@LCL +D=M +@SP +A=M +M=D +@SP +M=M+1 +@ARG +D=M +@SP +A=M +M=D +@SP +M=M+1 +@THIS +D=M +@SP +A=M +M=D +@SP +M=M+1 +@THAT +D=M +@SP +A=M +M=D +@SP +M=M+1 +@6 +D=D-A +@ARG +M=D +@SP +D=M +@LCL +M=D +@Sys.add12 +0;JMP +(_Sys.add12_RETURN_1) +@R5 +D=A +@R13 +M=D +@SP +AM=M-1 +D=M +@R13 +A=M +M=D +@LCL +D=M +@0 +A=D+A +D=M +@SP +A=M +M=D +@SP +M=M+1 +@LCL +D=M +@1 +A=D+A +D=M +@SP +A=M +M=D +@SP +M=M+1 +@LCL +D=M +@2 +A=D+A +D=M +@SP +A=M +M=D +@SP +M=M+1 +@LCL +D=M +@3 +A=D+A +D=M +@SP +A=M +M=D +@SP +M=M+1 +@LCL +D=M +@4 +A=D+A +D=M +@SP +A=M +M=D +@SP +M=M+1 +@SP +AM=M-1 +D=M +A=A-1 +M=M+D +@SP +AM=M-1 +D=M +A=A-1 +M=M+D +@SP +AM=M-1 +D=M +A=A-1 +M=M+D +@SP +AM=M-1 +D=M +A=A-1 +M=M+D +@LCL +D=M +@R13 +M=D +@5 +A=D-A +D=M +@R14 +M=D +@SP +AM=M-1 +D=M +@ARG +A=M +M=D +@ARG +D=M+1 +@SP +M=D +@R13 +AM=M-1 +D=M +@THAT +M=D +@R13 +AM=M-1 +D=M +@THIS +M=D +@R13 +AM=M-1 +D=M +@ARG +M=D +@R13 +AM=M-1 +D=M +@LCL +M=D +@R14 +A=M +0;JMP +(Sys.add12) +@4002 +D=A +@SP +A=M +M=D +@SP +M=M+1 +@THIS +D=A +@R13 +M=D +@SP +AM=M-1 +D=M +@R13 +A=M +M=D +@5002 +D=A +@SP +A=M +M=D +@SP +M=M+1 +@THAT +D=A +@R13 +M=D +@SP +AM=M-1 +D=M +@R13 +A=M +M=D +@ARG +D=M +@0 +A=D+A +D=M +@SP +A=M +M=D +@SP +M=M+1 +@12 +D=A +@SP +A=M +M=D +@SP +M=M+1 +@SP +AM=M-1 +D=M +A=A-1 +M=M+D +@LCL +D=M +@R13 +M=D +@5 +A=D-A +D=M +@R14 +M=D +@SP +AM=M-1 +D=M +@ARG +A=M +M=D +@ARG +D=M+1 +@SP +M=D +@R13 +AM=M-1 +D=M +@THAT +M=D +@R13 +AM=M-1 +D=M +@THIS +M=D +@R13 +AM=M-1 +D=M +@ARG +M=D +@R13 +AM=M-1 +D=M +@LCL +M=D +@R14 +A=M +0;JMP diff --git a/vmtranslator/testdata/FunctionCalls/NestedCall/Sys.vm b/vmtranslator/testdata/FunctionCalls/NestedCall/Sys.vm new file mode 100644 index 0000000..a922535 --- /dev/null +++ b/vmtranslator/testdata/FunctionCalls/NestedCall/Sys.vm @@ -0,0 +1,57 @@ +// Sys.vm. Tested by the NestedCall test script. +// Consists of three functions: Sys.init, Sys.main, and Sys.add12. + +// Calls Sys.main() and stores a return value in temp 1. +// Does not return (enters infinite loop). +// The VM implementation starts running this function, by default. +function Sys.init 0 + push constant 4000 // tests that THIS and THAT are handled correctly + pop pointer 0 + push constant 5000 + pop pointer 1 + call Sys.main 0 + pop temp 1 + label LOOP + goto LOOP + +// Sets locals 1, 2 and 3 to some values. Leaves locals 0 and 4 unchanged, +// to test that the 'function' VM command initliazes them to 0 (the test +// script sets them to -1 before this code starts running). +// Calls Sys.add12(123) and stores the return value (should be 135) in temp 0. +// Returns local 0 + local 1 + local 2 + local 3 + local 4 (should be 456), to +// confirm that locals were not mangled by the function call. +function Sys.main 5 + push constant 4001 + pop pointer 0 + push constant 5001 + pop pointer 1 + push constant 200 + pop local 1 + push constant 40 + pop local 2 + push constant 6 + pop local 3 + push constant 123 + call Sys.add12 1 + pop temp 0 + push local 0 + push local 1 + push local 2 + push local 3 + push local 4 + add + add + add + add + return + +// Returns (argument 0) + 12. +function Sys.add12 0 + push constant 4002 + pop pointer 0 + push constant 5002 + pop pointer 1 + push argument 0 + push constant 12 + add + return diff --git a/vmtranslator/testdata/FunctionCalls/SimpleFunction/SimpleFunction.asm b/vmtranslator/testdata/FunctionCalls/SimpleFunction/SimpleFunction.asm new file mode 100644 index 0000000..0bb6812 --- /dev/null +++ b/vmtranslator/testdata/FunctionCalls/SimpleFunction/SimpleFunction.asm @@ -0,0 +1,115 @@ +(SimpleFunction.test) +@0 +D=A +@SP +A=M +M=D +@SP +M=M+1 +@0 +D=A +@SP +A=M +M=D +@SP +M=M+1 +@LCL +D=M +@0 +A=D+A +D=M +@SP +A=M +M=D +@SP +M=M+1 +@LCL +D=M +@1 +A=D+A +D=M +@SP +A=M +M=D +@SP +M=M+1 +@SP +AM=M-1 +D=M +A=A-1 +M=M+D +@SP +A=M-1 +M=!M +@ARG +D=M +@0 +A=D+A +D=M +@SP +A=M +M=D +@SP +M=M+1 +@SP +AM=M-1 +D=M +A=A-1 +M=M+D +@ARG +D=M +@1 +A=D+A +D=M +@SP +A=M +M=D +@SP +M=M+1 +@SP +AM=M-1 +D=M +A=A-1 +M=M-D +@LCL +D=M +@R13 +M=D +@5 +A=D-A +D=M +@R14 +M=D +@SP +AM=M-1 +D=M +@ARG +A=M +M=D +@ARG +D=M+1 +@SP +M=D +@R13 +AM=M-1 +D=M +@THAT +M=D +@R13 +AM=M-1 +D=M +@THIS +M=D +@R13 +AM=M-1 +D=M +@ARG +M=D +@R13 +AM=M-1 +D=M +@LCL +M=D +@R14 +A=M +0;JMP diff --git a/vmtranslator/testdata/FunctionCalls/SimpleFunction/SimpleFunction.vm b/vmtranslator/testdata/FunctionCalls/SimpleFunction/SimpleFunction.vm new file mode 100644 index 0000000..ca6d1a5 --- /dev/null +++ b/vmtranslator/testdata/FunctionCalls/SimpleFunction/SimpleFunction.vm @@ -0,0 +1,17 @@ +// 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/08/FunctionCalls/SimpleFunction/SimpleFunction.vm + +// Performs a simple calculation and returns the result. +// argument[0] and argument[1] must be set by the caller of this code. +function SimpleFunction.test 2 + push local 0 + push local 1 + add + not + push argument 0 + add + push argument 1 + sub + return diff --git a/vmtranslator/testdata/FunctionCalls/StaticsTest/Class1.vm b/vmtranslator/testdata/FunctionCalls/StaticsTest/Class1.vm new file mode 100644 index 0000000..b05caaf --- /dev/null +++ b/vmtranslator/testdata/FunctionCalls/StaticsTest/Class1.vm @@ -0,0 +1,20 @@ +// 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/08/FunctionCalls/StaticsTest/Class1.vm + +// Stores two supplied arguments in static[0] and static[1]. +function Class1.set 0 + push argument 0 + pop static 0 + push argument 1 + pop static 1 + push constant 0 + return + +// Returns static[0] - static[1]. +function Class1.get 0 + push static 0 + push static 1 + sub + return diff --git a/vmtranslator/testdata/FunctionCalls/StaticsTest/Class2.vm b/vmtranslator/testdata/FunctionCalls/StaticsTest/Class2.vm new file mode 100644 index 0000000..1855145 --- /dev/null +++ b/vmtranslator/testdata/FunctionCalls/StaticsTest/Class2.vm @@ -0,0 +1,20 @@ +// 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/08/FunctionCalls/StaticsTest/Class2.vm + +// Stores two supplied arguments in static[0] and static[1]. +function Class2.set 0 + push argument 0 + pop static 0 + push argument 1 + pop static 1 + push constant 0 + return + +// Returns static[0] - static[1]. +function Class2.get 0 + push static 0 + push static 1 + sub + return diff --git a/vmtranslator/testdata/FunctionCalls/StaticsTest/StaticsTest.asm b/vmtranslator/testdata/FunctionCalls/StaticsTest/StaticsTest.asm new file mode 100644 index 0000000..cfd24cc --- /dev/null +++ b/vmtranslator/testdata/FunctionCalls/StaticsTest/StaticsTest.asm @@ -0,0 +1,236 @@ +(Sys.init) +@6 +D=A +@SP +A=M +M=D +@SP +M=M+1 +@8 +D=A +@SP +A=M +M=D +@SP +M=M+1 +@_Class1.set_RETURN_0 +D=A +@SP +A=M +M=D +@SP +M=M+1 +@LCL +D=M +@SP +A=M +M=D +@SP +M=M+1 +@ARG +D=M +@SP +A=M +M=D +@SP +M=M+1 +@THIS +D=M +@SP +A=M +M=D +@SP +M=M+1 +@THAT +D=M +@SP +A=M +M=D +@SP +M=M+1 +@7 +D=D-A +@ARG +M=D +@SP +D=M +@LCL +M=D +@Class1.set +0;JMP +(_Class1.set_RETURN_0) +@R5 +D=A +@R13 +M=D +@SP +AM=M-1 +D=M +@R13 +A=M +M=D +@23 +D=A +@SP +A=M +M=D +@SP +M=M+1 +@15 +D=A +@SP +A=M +M=D +@SP +M=M+1 +@_Class2.set_RETURN_1 +D=A +@SP +A=M +M=D +@SP +M=M+1 +@LCL +D=M +@SP +A=M +M=D +@SP +M=M+1 +@ARG +D=M +@SP +A=M +M=D +@SP +M=M+1 +@THIS +D=M +@SP +A=M +M=D +@SP +M=M+1 +@THAT +D=M +@SP +A=M +M=D +@SP +M=M+1 +@7 +D=D-A +@ARG +M=D +@SP +D=M +@LCL +M=D +@Class2.set +0;JMP +(_Class2.set_RETURN_1) +@R5 +D=A +@R13 +M=D +@SP +AM=M-1 +D=M +@R13 +A=M +M=D +@_Class1.get_RETURN_2 +D=A +@SP +A=M +M=D +@SP +M=M+1 +@LCL +D=M +@SP +A=M +M=D +@SP +M=M+1 +@ARG +D=M +@SP +A=M +M=D +@SP +M=M+1 +@THIS +D=M +@SP +A=M +M=D +@SP +M=M+1 +@THAT +D=M +@SP +A=M +M=D +@SP +M=M+1 +@5 +D=D-A +@ARG +M=D +@SP +D=M +@LCL +M=D +@Class1.get +0;JMP +(_Class1.get_RETURN_2) +@_Class2.get_RETURN_3 +D=A +@SP +A=M +M=D +@SP +M=M+1 +@LCL +D=M +@SP +A=M +M=D +@SP +M=M+1 +@ARG +D=M +@SP +A=M +M=D +@SP +M=M+1 +@THIS +D=M +@SP +A=M +M=D +@SP +M=M+1 +@THAT +D=M +@SP +A=M +M=D +@SP +M=M+1 +@5 +D=D-A +@ARG +M=D +@SP +D=M +@LCL +M=D +@Class2.get +0;JMP +(_Class2.get_RETURN_3) +(Sys.init$END) +@Sys.init$END +0;JMP diff --git a/vmtranslator/testdata/FunctionCalls/StaticsTest/Sys.vm b/vmtranslator/testdata/FunctionCalls/StaticsTest/Sys.vm new file mode 100644 index 0000000..e6f7043 --- /dev/null +++ b/vmtranslator/testdata/FunctionCalls/StaticsTest/Sys.vm @@ -0,0 +1,20 @@ +// 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/08/FunctionCalls/StaticsTest/Sys.vm + +// Tests that different functions, stored in two different +// class files, manipulate the static segment correctly. +function Sys.init 0 + push constant 6 + push constant 8 + call Class1.set 2 + pop temp 0 // dumps the return value + push constant 23 + push constant 15 + call Class2.set 2 + pop temp 0 // dumps the return value + call Class1.get 0 + call Class2.get 0 +label END + goto END diff --git a/vmtranslator/testdata/ProgramFlow/BasicLoop/BasicLoop.asm b/vmtranslator/testdata/ProgramFlow/BasicLoop/BasicLoop.asm new file mode 100644 index 0000000..2896595 --- /dev/null +++ b/vmtranslator/testdata/ProgramFlow/BasicLoop/BasicLoop.asm @@ -0,0 +1,116 @@ +@0 +D=A +@SP +A=M +M=D +@SP +M=M+1 +@LCL +D=M +@0 +D=D+A +@R13 +M=D +@SP +AM=M-1 +D=M +@R13 +A=M +M=D +($LOOP) +@ARG +D=M +@0 +A=D+A +D=M +@SP +A=M +M=D +@SP +M=M+1 +@LCL +D=M +@0 +A=D+A +D=M +@SP +A=M +M=D +@SP +M=M+1 +@SP +AM=M-1 +D=M +A=A-1 +M=M+D +@LCL +D=M +@0 +D=D+A +@R13 +M=D +@SP +AM=M-1 +D=M +@R13 +A=M +M=D +@ARG +D=M +@0 +A=D+A +D=M +@SP +A=M +M=D +@SP +M=M+1 +@1 +D=A +@SP +A=M +M=D +@SP +M=M+1 +@SP +AM=M-1 +D=M +A=A-1 +M=M-D +@ARG +D=M +@0 +D=D+A +@R13 +M=D +@SP +AM=M-1 +D=M +@R13 +A=M +M=D +@ARG +D=M +@0 +A=D+A +D=M +@SP +A=M +M=D +@SP +M=M+1 +@SP +AM=M-1 +D=M +@$LOOP +D;JNE +@LCL +D=M +@0 +A=D+A +D=M +@SP +A=M +M=D +@SP +M=M+1 diff --git a/vmtranslator/testdata/ProgramFlow/BasicLoop/BasicLoop.vm b/vmtranslator/testdata/ProgramFlow/BasicLoop/BasicLoop.vm new file mode 100644 index 0000000..ae99435 --- /dev/null +++ b/vmtranslator/testdata/ProgramFlow/BasicLoop/BasicLoop.vm @@ -0,0 +1,23 @@ +// 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/08/ProgramFlow/BasicLoop/BasicLoop.vm + +// Computes the sum 1 + 2 + ... + n and pushes the result onto +// the stack. The value n is given in argument[0], which must be +// initialized by the caller of this code. + + push constant 0 + pop local 0 // sum = 0 +label LOOP + push argument 0 + push local 0 + add + pop local 0 // sum = sum + n + push argument 0 + push constant 1 + sub + pop argument 0 // n-- + push argument 0 + if-goto LOOP // if n > 0, goto LOOP + push local 0 // else, pushes sum to the stack's top diff --git a/vmtranslator/testdata/ProgramFlow/FibonacciSeries/FibonacciSeries.asm b/vmtranslator/testdata/ProgramFlow/FibonacciSeries/FibonacciSeries.asm new file mode 100644 index 0000000..43f847a --- /dev/null +++ b/vmtranslator/testdata/ProgramFlow/FibonacciSeries/FibonacciSeries.asm @@ -0,0 +1,214 @@ +@ARG +D=M +@1 +A=D+A +D=M +@SP +A=M +M=D +@SP +M=M+1 +@THAT +D=A +@R13 +M=D +@SP +AM=M-1 +D=M +@R13 +A=M +M=D +@0 +D=A +@SP +A=M +M=D +@SP +M=M+1 +@THAT +D=M +@0 +D=D+A +@R13 +M=D +@SP +AM=M-1 +D=M +@R13 +A=M +M=D +@1 +D=A +@SP +A=M +M=D +@SP +M=M+1 +@THAT +D=M +@1 +D=D+A +@R13 +M=D +@SP +AM=M-1 +D=M +@R13 +A=M +M=D +@ARG +D=M +@0 +A=D+A +D=M +@SP +A=M +M=D +@SP +M=M+1 +@2 +D=A +@SP +A=M +M=D +@SP +M=M+1 +@SP +AM=M-1 +D=M +A=A-1 +M=M-D +@ARG +D=M +@0 +D=D+A +@R13 +M=D +@SP +AM=M-1 +D=M +@R13 +A=M +M=D +($LOOP) +@ARG +D=M +@0 +A=D+A +D=M +@SP +A=M +M=D +@SP +M=M+1 +@SP +AM=M-1 +D=M +@$COMPUTE_ELEMENT +D;JNE +@$END +0;JMP +($COMPUTE_ELEMENT) +@THAT +D=M +@0 +A=D+A +D=M +@SP +A=M +M=D +@SP +M=M+1 +@THAT +D=M +@1 +A=D+A +D=M +@SP +A=M +M=D +@SP +M=M+1 +@SP +AM=M-1 +D=M +A=A-1 +M=M+D +@THAT +D=M +@2 +D=D+A +@R13 +M=D +@SP +AM=M-1 +D=M +@R13 +A=M +M=D +@THAT +D=M +@SP +A=M +M=D +@SP +M=M+1 +@1 +D=A +@SP +A=M +M=D +@SP +M=M+1 +@SP +AM=M-1 +D=M +A=A-1 +M=M+D +@THAT +D=A +@R13 +M=D +@SP +AM=M-1 +D=M +@R13 +A=M +M=D +@ARG +D=M +@0 +A=D+A +D=M +@SP +A=M +M=D +@SP +M=M+1 +@1 +D=A +@SP +A=M +M=D +@SP +M=M+1 +@SP +AM=M-1 +D=M +A=A-1 +M=M-D +@ARG +D=M +@0 +D=D+A +@R13 +M=D +@SP +AM=M-1 +D=M +@R13 +A=M +M=D +@$LOOP +0;JMP +($END) diff --git a/vmtranslator/testdata/ProgramFlow/FibonacciSeries/FibonacciSeries.vm b/vmtranslator/testdata/ProgramFlow/FibonacciSeries/FibonacciSeries.vm new file mode 100644 index 0000000..a567d90 --- /dev/null +++ b/vmtranslator/testdata/ProgramFlow/FibonacciSeries/FibonacciSeries.vm @@ -0,0 +1,44 @@ +// 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/08/ProgramFlow/FibonacciSeries/FibonacciSeries.vm + +// Puts the first n elements of the Fibonacci series in the memory, +// starting at address addr. n and addr are given in argument[0] and +// argument[1], which must be initialized by the caller of this code. + + push argument 1 // sets THAT, the base address of the + pop pointer 1 // that segment, to argument[1] + push constant 0 // sets the series' first and second + pop that 0 // elements to 0 and 1, respectively + push constant 1 + pop that 1 + push argument 0 // sets n, the number of remaining elements + push constant 2 // to be computed to argument[0] minus 2, + sub // since 2 elements were already computed. + pop argument 0 + +label LOOP + push argument 0 + if-goto COMPUTE_ELEMENT // if n > 0, goto COMPUTE_ELEMENT + goto END // otherwise, goto END + +label COMPUTE_ELEMENT + // that[2] = that[0] + that[1] + push that 0 + push that 1 + add + pop that 2 + // THAT += 1 (updates the base address of that) + push pointer 1 + push constant 1 + add + pop pointer 1 + // updates n-- and loops + push argument 0 + push constant 1 + sub + pop argument 0 + goto LOOP + +label END diff --git a/vmtranslator/testdata/StackArithmetic/StackTest/StackTest.asm b/vmtranslator/testdata/StackArithmetic/StackTest/StackTest.asm index f08d08a..ced3625 100644 --- a/vmtranslator/testdata/StackArithmetic/StackTest/StackTest.asm +++ b/vmtranslator/testdata/StackArithmetic/StackTest/StackTest.asm @@ -316,7 +316,7 @@ D=M A=A-1 M=M-D @SP -AM=M-1 +A=M-1 M=-M @SP AM=M-1 @@ -336,5 +336,5 @@ D=M A=A-1 M=M|D @SP -AM=M-1 +A=M-1 M=!M