def test_zero8(run): assert run(Zero8, in_=0).out == True z = run(Zero8) for i in range(1, 256): z.in_ = i assert z.out == False
def test_dec16(): assert run(Dec16, in_=1).out == 0 assert run(Dec16, in_=0).out == -1 assert run(Dec16, in_=12345).out == 12344 assert run(Dec16, in_=-23456).out == -23457 assert run(Dec16, in_=-32768).out == 32767 assert gate_count(Dec16) == {'nands': 76}
def test_not16(): assert unsigned(run( Not16, in_=0b0000_0000_0000_0000).out) == 0b1111_1111_1111_1111 assert unsigned(run( Not16, in_=0b1111_1111_1111_1111).out) == 0b0000_0000_0000_0000 assert unsigned(run( Not16, in_=0b1010_1010_1010_1010).out) == 0b0101_0101_0101_0101 assert unsigned(run( Not16, in_=0b0011_1100_1100_0011).out) == 0b1100_0011_0011_1100 assert unsigned(run( Not16, in_=0b0001_0010_0011_0100).out) == 0b1110_1101_1100_1011
def test_dmux(): dmux00 = run(DMux, in_=0, sel=0) assert dmux00.a == 0 and dmux00.b == 0 dmux01 = run(DMux, in_=0, sel=1) assert dmux01.a == 0 and dmux01.b == 0 dmux10 = run(DMux, in_=1, sel=0) assert dmux10.a == 1 and dmux10.b == 0 dmux11 = run(DMux, in_=1, sel=1) assert dmux11.a == 0 and dmux11.b == 1
def test_halfAdder(): result = run(HalfAdder, a=0, b=0) assert result.sum == 0 and result.carry == 0 result = run(HalfAdder, a=0, b=1) assert result.sum == 1 and result.carry == 0 result = run(HalfAdder, a=1, b=0) assert result.sum == 1 and result.carry == 0 result = run(HalfAdder, a=1, b=1) assert result.sum == 0 and result.carry == 1
def test_mux16(): assert unsigned( run(Mux16, a=0b0000_0000_0000_0000, b=0b0000_0000_0000_0000, sel=0).out) == 0b0000_0000_0000_0000 assert unsigned( run(Mux16, a=0b0000_0000_0000_0000, b=0b0000_0000_0000_0000, sel=1).out) == 0b0000_0000_0000_0000 assert unsigned( run(Mux16, a=0b0000_0000_0000_0000, b=0b0001_0010_0011_0100, sel=0).out) == 0b0000_0000_0000_0000 assert unsigned( run(Mux16, a=0b0000_0000_0000_0000, b=0b0001_0010_0011_0100, sel=1).out) == 0b0001_0010_0011_0100 assert unsigned( run(Mux16, a=0b1001_1000_0111_0110, b=0b0000_0000_0000_0000, sel=0).out) == 0b1001_1000_0111_0110 assert unsigned( run(Mux16, a=0b1001_1000_0111_0110, b=0b0000_0000_0000_0000, sel=1).out) == 0b0000_0000_0000_0000 assert unsigned( run(Mux16, a=0b1010_1010_1010_1010, b=0b0101_0101_0101_0101, sel=0).out) == 0b1010_1010_1010_1010 assert unsigned( run(Mux16, a=0b1010_1010_1010_1010, b=0b0101_0101_0101_0101, sel=1).out) == 0b0101_0101_0101_0101
def test_mux4way16(): for i in range(4): assert run(Mux4Way16, a=0, b=0, c=0, d=0, sel=i).out == 0 assert unsigned( run(Mux4Way16, a=11111, b=22222, c=33333, d=44444, sel=0b00).out) == 11111 assert unsigned( run(Mux4Way16, a=11111, b=22222, c=33333, d=44444, sel=0b01).out) == 22222 assert unsigned( run(Mux4Way16, a=11111, b=22222, c=33333, d=44444, sel=0b10).out) == 33333 assert unsigned( run(Mux4Way16, a=11111, b=22222, c=33333, d=44444, sel=0b11).out) == 44444
def test_shiftR16(): assert run(ShiftR16, in_=0).out == 0 assert run(ShiftR16, in_=1).out == 0 assert run(ShiftR16, in_=2).out == 1 assert run(ShiftR16, in_=3).out == 1 # truncates as expected assert run(ShiftR16, in_=12345).out == 6172 assert run(ShiftR16, in_=-23456).out == -11728 assert run(ShiftR16, in_=-4).out == -2 assert run(ShiftR16, in_=-2).out == -1 # These two might be unexpected, but this is what you get: floor instead of truncate. assert run(ShiftR16, in_=-3).out == -2 assert run(ShiftR16, in_=-1).out == -1 assert gate_count(ShiftR16) == {} # No gates: this is just wiring.
def test_memory_access_static(chip=project_05.Computer, assemble=project_06.assemble, translator=project_07.Translator, simulator='codegen'): translate = translator() # Executes pop and push commands using the static segment. translate.push_constant(111) translate.push_constant(333) translate.push_constant(888) translate.pop_static(8) translate.pop_static(3) translate.pop_static(1) translate.push_static(3) translate.push_static(1) translate.sub() translate.push_static(8) translate.add() translate.finish() computer = run(chip, simulator=simulator) init_sp(computer) translate.asm.run(assemble, computer, debug=True) assert computer.peek(256) == 1110
def test_dmux4way(): for sel in range(4): result = run(DMux4Way, in_=0, sel=sel) assert result.a == 0 and result.b == 0 and result.c == 0 and result.d == 0 result = run(DMux4Way, in_=1, sel=0b00) assert result.a == 1 and result.b == 0 and result.c == 0 and result.d == 0 result = run(DMux4Way, in_=1, sel=0b01) assert result.a == 0 and result.b == 1 and result.c == 0 and result.d == 0 result = run(DMux4Way, in_=1, sel=0b10) assert result.a == 0 and result.b == 0 and result.c == 1 and result.d == 0 result = run(DMux4Way, in_=1, sel=0b11) assert result.a == 0 and result.b == 0 and result.c == 0 and result.d == 1
def cycles_per_second(chip, cycles_per_instr=1): """Estimate the speed of CPU simulation by running Max repeatedly with random input. """ import random import timeit computer = run(chip) computer.init_rom(MAX_PROGRAM) CYCLES = 14*cycles_per_instr def once(): x = random.randint(0, 0x7FFF) y = random.randint(0, 0x7FFF) computer.reset_program() computer.poke(1, x) computer.poke(2, y) for _ in range(CYCLES): computer.ticktock() assert computer.peek(3) == max(x, y) count, time = timeit.Timer(once).autorange() return count*CYCLES/time
def test_register8(run): reg = run(Register8) reg.in_ = 0 reg.load = 0 reg.tick(); reg.tock() assert reg.out == 0 reg.load = 1 reg.tick(); reg.tock() assert reg.out == 0 reg.in_ = 123 reg.load = 0 reg.tick(); reg.tock() assert reg.out == 0 reg.in_ = 111 reg.load = 0 reg.tick(); reg.tock() assert reg.out == 0 reg.in_ = 123 reg.load = 1 reg.tick(); reg.tock() assert reg.out == 123 reg.load = 0 reg.tick(); reg.tock() assert reg.out == 123 reg.in_ = -1 reg.tick(); reg.tock() assert reg.out == 123
def test_sys_lib(sys_class=project_12.SYS_CLASS, platform=BUNDLED_PLATFORM, simulator='codegen'): sys_test = _parse_jack_file("examples/project_12/SysTest.jack", platform) translator = platform.translator() translator.preamble() translate_jack(translator, platform, sys_class) # Dependencies: translate_library(translator, platform, "Memory") translate_library(translator, platform, "Math") translate_library(translator, platform, "Screen") translate_library(translator, platform, "Output") translate_library(translator, platform, "Keyboard") translate_library(translator, platform, "Array") translate_library(translator, platform, "String") translate_jack(translator, platform, sys_test) translator.finish() translator.check_references() computer = run(platform.chip, simulator=simulator) # translator.asm.run(platform.assemble, computer, stop_cycles=10_000, debug=True) translator.asm.trace(platform.assemble, computer, stop_cycles=1_000_000) # TODO: assert what? assert False
def test_computer_no_program(chip=project_05.Computer): computer = run(chip) for _ in range(100): computer.ticktock() assert computer.pc == 100
def test_bit_fine(): bit = run(Bit) assert bit.out == 0 bit.load = 1 bit.in_ = 1 assert bit.out == 0 # New value not visible on the output yet # bit.load = 0 # bit.in_ = 0 # assert bit.out == 0 # Still not visible, and clearing data has no affect after store is lowered bit.tick() assert bit.out == 0 # No change bit.tock() assert bit.out == 1 # Now you can see the new value bit.tick() assert bit.out == 1 # Still no change bit.tock() assert bit.out == 1 # Still no change bit.load = 1 bit.in_ = 0 assert bit.out == 1 # Update, but not exposed yet bit.tick() assert bit.out == 1 # No change bit.tock() assert bit.out == 0 # Now you can see the new value
def test_compare_edge_cases(chip=project_05.Computer, assemble=project_06.assemble, translator=project_07.Translator, simulator='codegen'): translate = translator() # -1,000 < 2,000: the difference fits in a word translate.push_constant(1000) translate.neg() translate.push_constant(2000) translate.lt() # -20,000 < 30,000: the difference overflows translate.push_constant(20000) translate.neg() translate.push_constant(30000) translate.lt() translate.finish() computer = run(chip, simulator=simulator) init_sp(computer) translate.asm.run(assemble, computer, stop_cycles=1_000, debug=True) assert computer.sp == 258 assert computer.peek(256) == -1 assert computer.peek(257) == -1
def test_bit_coarse(): bit = run(Bit) assert bit.out == 0 bit.load = 1 bit.in_ = 1 assert bit.out == 0 # New value not visible on the output yet bit.tick() bit.tock() assert bit.out == 1 # Now you can see the new value bit.load = 0 bit.in_ = 0 bit.tick() bit.tock() assert bit.out == 1 # No change bit.load = 1 bit.in_ = 0 assert bit.out == 1 # Not updated yet bit.tick() bit.tock() assert bit.out == 0 # Now you can see the new value
def test_array_lib(array_class=project_12.ARRAY_CLASS, platform=BUNDLED_PLATFORM, simulator='codegen'): # Note: this one's not so useful interactively, but easier to view anyway array_test = _parse_jack_file("examples/project_12/ArrayTest.jack", platform) translator = platform.translator() translator.preamble() translate_jack(translator, platform, array_class) _translate_dependencies(translator, platform, ["Memory"]) translate_jack(translator, platform, array_test) translator.finish() translator.check_references() computer = run(platform.chip, simulator=simulator) translator.asm.run(platform.assemble, computer, stop_cycles=10_000, debug=True) assert computer.peek(8000) == 222 assert computer.peek(8001) == 122 assert computer.peek(8002) == 100 assert computer.peek(8003) == 10
def test_mux(): assert run(Mux, a=0, b=0, sel=0).out == 0 assert run(Mux, a=0, b=0, sel=1).out == 0 assert run(Mux, a=0, b=1, sel=0).out == 0 assert run(Mux, a=0, b=1, sel=1).out == 1 assert run(Mux, a=1, b=0, sel=0).out == 1 assert run(Mux, a=1, b=0, sel=1).out == 0 assert run(Mux, a=1, b=1, sel=0).out == 1 assert run(Mux, a=1, b=1, sel=1).out == 1
def test_inc8(run): assert run(Inc8, in_= 0, carry_in=0).out == 0 assert run(Inc8, in_= 5, carry_in=0).out == 5 assert run(Inc8, in_=255, carry_in=0).out == 255 assert run(Inc8, in_= 0, carry_in=1).out == 1 assert run(Inc8, in_= 5, carry_in=1).out == 6 assert run(Inc8, in_=255, carry_in=1).out == 0 # TODO: what to do with negatives? Probably need to treat these value as unsigned until they're # re-assembled into 16-bits. # assert run(Inc8, in_=-1).out == 0 # assert run(Inc8, in_=-5).out == -4 assert run(Inc8, in_= 0, carry_in=0).carry_out == 0 assert run(Inc8, in_=255, carry_in=0).carry_out == 0 assert run(Inc8, in_= 0, carry_in=1).carry_out == 0 assert run(Inc8, in_=255, carry_in=1).carry_out == 1
def test_add8(run): assert run(Add8, a= 0, b= 0, carry_in=0).out == 0 assert run(Add8, a=255, b=255, carry_in=0).out == 254 assert run(Add8, a= 0, b= 0, carry_in=1).out == 1 assert run(Add8, a=255, b=255, carry_in=1).out == 255 assert run(Add8, a= 0, b= 0, carry_in=0).carry_out == False assert run(Add8, a=255, b=255, carry_in=0).carry_out == True assert run(Add8, a= 0, b= 0, carry_in=1).carry_out == False assert run(Add8, a=255, b=255, carry_in=1).carry_out == True
def test_statics_multiple_files(chip=project_05.Computer, assemble=project_06.assemble, translator=project_08.Translator, simulator='codegen'): """Tests that different functions, stored in two different classes, manipulate the static segment correctly. Note: the original materials actually refer to _files_ not _classes_, but I think in the context of this language there's a one-to-one correspondence. """ translate = translator() # Note: seems like this should just happen, but the previous tests actually require it _not_ to. translate.preamble() # Sys.vm: translate.function("Sys", "init", 0) translate.push_constant(6) translate.push_constant(8) translate.call("Class1", "set", 2) translate.pop_temp(0) # Dumps the return value translate.push_constant(23) translate.push_constant(15) translate.call("Class2", "set", 2) translate.pop_temp(0) # Dumps the return value translate.call("Class1", "get", 0) translate.call("Class2", "get", 0) translate.label("WHILE") translate.goto("WHILE") for class_name in ["Class1", "Class2"]: # Stores two supplied arguments in static[0] and static[1]. translate.function(class_name, "set", 0) translate.push_argument(0) translate.pop_static(0) translate.push_argument(1) translate.pop_static(1) translate.push_constant(0) translate.return_op() # Returns static[0] - static[1]. translate.function(class_name, "get", 0) translate.push_static(0) translate.push_static(1) translate.sub() translate.return_op() translate.finish() computer = run(chip, simulator=simulator) # Note: no initialization this time translate.asm.run(assemble, computer, stop_cycles=2500, debug=True) assert computer.sp == 263 assert computer.peek(261) == -2 assert computer.peek(262) == 8
def test_screen_lib(screen_class=project_12.SCREEN_CLASS, platform=BUNDLED_PLATFORM, simulator='codegen'): screen_test = _parse_jack_file("examples/project_12/ScreenTest.jack", platform) translator = platform.translator() translator.preamble() translate_jack(translator, platform, screen_class) # Dependencies; note: need Sys.init to intialize Screen, but don't want the built-in implementation. translate_library(translator, platform, "Array") translate_library(translator, platform, "Memory") translate_library(translator, platform, "Math") translate_jack(translator, platform, minimal_sys_lib(["Memory", "Math", "Screen"], platform)) translate_jack(translator, platform, screen_test) translator.finish() translator.check_references() computer = run(platform.chip, simulator=simulator) translator.asm.run(platform.assemble, computer, stop_cycles=10_000_000, debug=True) # translator.asm.run(platform.assemble, computer, stop_cycles=100_000, debug=True) # translator.asm.trace(platform.assemble, computer, stop_cycles=10_000_000) # translator.asm.trace(platform.assemble, computer, stop_cycles=100_000) dump_screen(computer) # For debugging # If you have failures here, try running # `./computer.py examples/project_12/ScreenTest.jack` and see if looks like a house. # If later assertions fail, maybe your impl is too slow and it's just not finished after # 10 million cycles. See the trace logging. # Spot check some groups of pixels: assert [computer.peek_screen(220 * 32 + w) for w in range(32)] == [-1] * 32, "ground all filled" assert computer.peek_screen(100 * 32 + 20) == -1, "house filled" assert computer.peek_screen(160 * 32 + 22) == 0, "door cleared" assert computer.peek_screen( 64 * 32 + 19) != 0, "along left roof line (at least one pixel)" assert computer.peek_screen( 50 * 32 + 22) != 0, "along right roof line (at least one pixel)" assert computer.peek_screen(6 * 32 + 8) == 1 << 12, "12 o-clock ray tip" assert computer.peek_screen(114 * 32 + 8) == 1 << 12, "6 o-clock ray tip" assert computer.peek_screen(60 * 32 + 5) == -64, "9 o-clock ray tip" assert computer.peek_screen(60 * 32 + 12) == 7, "3 o-clock ray tip"
def test_write_sp(): """Writing to address 0 using Hack instructions updates SP.""" cpu = run(SPCPU) init_sp(cpu) assert cpu.writeM == False # to avoid confusion, the RAM is not also written assert cpu.sp == 256
def test_memory_access_basic(chip=project_05.Computer, assemble=project_06.assemble, translator=project_07.Translator, simulator='codegen'): translate = translator() # Executes pop and push commands using the virtual memory segments. translate.push_constant(10) translate.pop_local(0) translate.push_constant(21) translate.push_constant(22) translate.pop_argument(2) translate.pop_argument(1) translate.push_constant(36) translate.pop_this(6) translate.push_constant(42) translate.push_constant(45) translate.pop_that(5) translate.pop_that(2) translate.push_constant(510) translate.pop_temp(6) translate.push_local(0) translate.push_that(5) translate.add() translate.push_argument(1) translate.sub() translate.push_this(6) translate.push_this(6) translate.add() translate.sub() translate.push_temp(6) translate.add() translate.finish() computer = run(chip, simulator=simulator) init_sp(computer) computer.poke(1, 255) # base address of the local segment computer.poke(2, 247) # base address of the argument segment computer.poke(3, 3000) # base address of the this segment computer.poke(4, 3010) # base address of the that segment translate.asm.run(assemble, computer, debug=True) # Note: the original test put the stack in a funky state with LCL and ARG are _above_ SP, # which actually makes no sense and as a result the trace was confusing, so here they're # set to realistic values. assert computer.peek(256) == 472 assert computer.peek(255) == 10 assert computer.peek(248) == 21 assert computer.peek(249) == 22 assert computer.peek(3006) == 36 assert computer.peek(3012) == 42 assert computer.peek(3015) == 45 assert computer.peek(11) == 510
def test_eq16(simulator): assert run(Eq16, a=1, b=0, simulator=simulator).out == False assert run(Eq16, a=0, b=0, simulator=simulator).out == True assert run(Eq16, a=12345, b=12345, simulator=simulator).out == True assert run(Eq16, a=-23456, b=-23456, simulator=simulator).out == True assert run(Eq16, a=-32768, b=-32768, simulator=simulator).out == True for i in range(16): assert run(Eq16, a=(1 << i), b=(1 << i), simulator=simulator).out == True assert run(Eq16, a=(1 << i), b=0, simulator=simulator).out == False assert run(Eq16, a=0, b=(1 << i), simulator=simulator).out == False
def test_fill(Computer=solved_05.Computer, assemble=solved_06.assemble): # We're going to run a few million cycles, so the faster simulator is a better option: computer = run(Computer, simulator='codegen') pgm, _, _ = assemble(FILL_ASM) computer.init_rom(pgm) computer.set_keydown(0) # the keyboard is untouched for _ in range(1_000_000): computer.ticktock()
def test_program_complex_arrays(): # Isolate the compiler by using the included solution for everything else: platform = BUNDLED_PLATFORM simulator = "codegen" with open("examples/project_11/ComplexArrays/Main.jack") as f: src = f.read() ast = platform.parser(src) asm = AssemblySource() project_11.compile_class(ast, asm) # If it fails, you probably want to see the opcodes it wrote: for l in asm.lines: print(l) ops = [ platform.parse_line(l) for l in asm.lines if platform.parse_line(l) is not None ] translator = platform.translator() translator.preamble() for op in ops: translator.handle(op) # Note: using the full OS implementation is simpler then the fancy tricks done in test_12 # to isolate individual OS classes, but it also means that this test might need millions of # cycles to run, including writing all the results to the screen buffer. translate_library(translator, platform) translator.finish() translator.check_references() computer = run(platform.chip, simulator=simulator) output_stream = StringWriter() translator.asm.run(platform.assemble, computer, stop_cycles=5_000_000, debug=True, tty=output_stream) output_lines = "".join(output_stream.strs).split("\n") assert output_lines == [ "Test 1: expected result: 5; actual result: 5", "Test 2: expected result: 40; actual result: 40", "Test 3: expected result: 0; actual result: 0", "Test 4: expected result: 77; actual result: 77", "Test 5: expected result: 110; actual result: 110", "", ]
def test_dmux8way(): for sel in range(8): result = run(DMux8Way, in_=0, sel=sel) assert (result.a == 0 and result.b == 0 and result.c == 0 and result.d == 0 and result.e == 0 and result.f == 0 and result.g == 0 and result.h == 0) result = run(DMux8Way, in_=1, sel=0b000) assert (result.a == 1 and result.b == 0 and result.c == 0 and result.d == 0 and result.e == 0 and result.f == 0 and result.g == 0 and result.h == 0) result = run(DMux8Way, in_=1, sel=0b001) assert (result.a == 0 and result.b == 1 and result.c == 0 and result.d == 0 and result.e == 0 and result.f == 0 and result.g == 0 and result.h == 0) result = run(DMux8Way, in_=1, sel=0b010) assert (result.a == 0 and result.b == 0 and result.c == 1 and result.d == 0 and result.e == 0 and result.f == 0 and result.g == 0 and result.h == 0) result = run(DMux8Way, in_=1, sel=0b011) assert (result.a == 0 and result.b == 0 and result.c == 0 and result.d == 1 and result.e == 0 and result.f == 0 and result.g == 0 and result.h == 0) result = run(DMux8Way, in_=1, sel=0b100) assert (result.a == 0 and result.b == 0 and result.c == 0 and result.d == 0 and result.e == 1 and result.f == 0 and result.g == 0 and result.h == 0) result = run(DMux8Way, in_=1, sel=0b101) assert (result.a == 0 and result.b == 0 and result.c == 0 and result.d == 0 and result.e == 0 and result.f == 1 and result.g == 0 and result.h == 0) result = run(DMux8Way, in_=1, sel=0b110) assert (result.a == 0 and result.b == 0 and result.c == 0 and result.d == 0 and result.e == 0 and result.f == 0 and result.g == 1 and result.h == 0) result = run(DMux8Way, in_=1, sel=0b111) assert (result.a == 0 and result.b == 0 and result.c == 0 and result.d == 0 and result.e == 0 and result.f == 0 and result.g == 0 and result.h == 1)
def test_computer_tty_no_program(chip=project_05.Computer, simulator="vector"): """When nothing has been written address 0x6000, no value is available on the TTY "port". """ computer = run(chip, simulator=simulator) for _ in range(100): computer.ticktock() assert computer.pc == 100 assert computer.tty_ready == True assert computer.get_tty() == 0