From f62467f4281e293169e238473f6e966a7f53f242 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Mon, 15 Jan 2024 21:53:25 -0500 Subject: [PATCH 001/221] Make the Vector wrapper more flexible Handle presence or absence of ROM, RAM(s), etc. separately, and fail lazily. This allows alternate implementations pick and choose which components to use. --- nand/vector.py | 124 ++++++++++++++++++++++++++++++++++++------------- test_05.py | 1 + 2 files changed, 94 insertions(+), 31 deletions(-) diff --git a/nand/vector.py b/nand/vector.py index 6828a17..dea75cd 100644 --- a/nand/vector.py +++ b/nand/vector.py @@ -27,11 +27,7 @@ def run(ic, optimize = True): if optimize: ic = simplify(ic) nv, stateful = synthesize(ic) - if any(isinstance(c, ROM) for c in ic.sorted_components()): - w = NandVectorComputerWrapper(nv, stateful) - else: - w = NandVectorWrapper(nv) - return w + return NandVectorWrapper(nv, stateful) def synthesize(ic): @@ -519,11 +515,40 @@ def custom_op(f): class NandVectorWrapper: - """Convenient syntax around a NandVector. You get one of these from run(chip). + """Wrapper with extra operations for the full Computer, when the expected components are found. + + If there are any ROMs, init_rom operates on one, chosen arbitrarily. + + If there are any RAMs, peek/poke operate on the largest RAM. + If there is more than one RAM, peek_/poke_screen operate on the *second* largest RAM. + + If there is a ROM and an output "pc", run_program is can be used to run a sequence of + instructions completely. + + If there is an input "reset", reset_program will use it to reset the processor state. + + If there is any Input, set_keydown will set the keycode that can be read through it. + + If there is any Output, get_tty will read the last character written to it, and reset it's + "ready" flag. """ - def __init__(self, vector): + def __init__(self, vector, stateful): self._vector = vector + self._stateful = stateful + + def nth(seq, n): + lst = list(seq) + if len(lst) > n: + return lst[n] + + self._rom = nth((c for c in self._stateful if isinstance(c, ROMOps)), 0) + rams_by_size_desc = sorted((c for c in self._stateful if isinstance(c, RAMOps)), + key=lambda c: c.comp.address_bits, reverse=True) + self._mem = nth(rams_by_size_desc, 0) + self._screen = nth(rams_by_size_desc, 1) + self._keyboard = nth((c for c in self._stateful if isinstance(c, InputOps)), 0) + self._tty = nth((c for c in self._stateful if isinstance(c, OutputOps)), 0) def __setattr__(self, name, value): """Set the value of a single- or multiple-bit input.""" @@ -583,33 +608,15 @@ def outputs(self): def internal(self): return dict([(name, self.get_internal(name)) for (name, _) in self._vector.internal.keys()]) - def components(self, types): - """List of internal components (e.g. RAM, ROM). - - Note: types should be one or more of the types defined in nand.component, not the wrappers - with the same names defined in this module. - """ - return [c for c in self._components if isinstance(c, types)] - - def __repr__(self): - return str(self.outputs()) - - -class NandVectorComputerWrapper(NandVectorWrapper): - """Wrapper with extra operations for the full Computer.""" - def __init__(self, vector, stateful): - NandVectorWrapper.__init__(self, vector) - self._stateful = stateful - self._rom, = [c for c in self._stateful if isinstance(c, ROMOps)] - self._mem, = [c for c in self._stateful if isinstance(c, RAMOps) and c.comp.address_bits == 14] - self._screen, = [c for c in self._stateful if isinstance(c, RAMOps) and c.comp.address_bits == 13] - self._keyboard, = [c for c in self._stateful if isinstance(c, InputOps)] - self._tty, = [c for c in self._stateful if isinstance(c, OutputOps)] + # High-level def run_program(self, instructions): """Install and run a sequence of instructions, stopping when pc runs off the end.""" + if ('pc', 0) not in self._vector.outputs: + raise MissingComponent("'pc' not present (or not exposed as an output)") + self.init_rom(instructions) while self.pc <= len(instructions): @@ -618,6 +625,9 @@ def run_program(self, instructions): def reset_program(self): """Reset pc so the program will run again from the top.""" + if ('reset', 0) not in self._vector.inputs: + raise MissingComponent("No 'reset' input present") + self.reset = 1 self.ticktock() self.reset = 0 @@ -629,6 +639,9 @@ def init_rom(self, instructions): after the program, which could in theory be used to detect termination. """ + if self._rom is None: + raise MissingComponent("No ROM present") + size = len(instructions) # The ROM size limits the size of program that can run, not to mention, e.g. the format of @@ -645,27 +658,56 @@ def init_rom(self, instructions): def peek(self, address): """Read a single word from the Computer's memory.""" + + if self._mem is None: + raise MissingComponent("No RAM present") + return self._mem.storage[address] def poke(self, address, value): """Write a single word to the Computer's memory.""" + + if self._mem is None: + raise MissingComponent("No RAM present") + self._mem.storage[address] = value def peek_screen(self, address): - """Read a value from the display RAM. Address must be between 0x000 and 0x1FFF.""" + """Read a value from the separate display RAM. + + Assuming the standard memory layout, address must be between 0x000 and 0x1FFF. + """ + + if self._screen is None: + raise MissingComponent("No separate screen RAM present") + return self._screen.storage[address] def poke_screen(self, address, value): - """Write a value to the display RAM. Address must be between 0x000 and 0x1FFF.""" + """Write a value to the separate display RAM. + + Assuming the standard memory layout, address must be between 0x000 and 0x1FFF. + """ + + if self._screen is None: + raise MissingComponent("No separate screen RAM present") + self._screen.storage[address] = value def set_keydown(self, keycode): """Provide the code which identifies a single key which is currently pressed.""" + + if self._keyboard is None: + raise MissingComponent("No Input present") + self._keyboard.set(keycode) def get_tty(self): """Read one word of output which has been written to the tty port, and reset it to 0.""" + if self._tty is None: + raise MissingComponent("No Output present") + # Tricky: clearing the value in the Output component flips its `ready` output, most of the time. self._vector.dirty = True return self._tty.read() @@ -676,3 +718,23 @@ def get_tty(self): def sp(self): """Read the current value of the stack pointer, which is normally stored at RAM[0].""" return self.peek(0) + + def __repr__(self): + return str(self.outputs()) + + +class MissingComponent(Exception): + def __init__(self, msg): + Exception.__init__(self, msg) + + +# def has_rom(ic): +# """Check for presence of exactly one ROM (any size).""" +# roms = [c for c in ic.sorted_components() if isinstance(c, ROM)] +# return len(roms) == 1 + + +# def has_ram(ic, address_bits): +# """Check for presence of exactly one RAM with the expected size.""" +# rams = [c for c in ic.sorted_components() if isinstance(c, RAM) and c.address_bits == address_bits] +# return len(rams) == 1 diff --git a/test_05.py b/test_05.py index b0c9f11..5dc6798 100755 --- a/test_05.py +++ b/test_05.py @@ -106,6 +106,7 @@ def test_memory_system(): mem.tick(); mem.tock() assert mem.out == -1 + # ...load still set mem.address = 0x504f mem.tick(); mem.tock() assert mem.out == -1 From 7cf974913f599c562e5777cc8c77d03bdd714082 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Mon, 15 Jan 2024 23:26:28 -0500 Subject: [PATCH 002/221] =?UTF-8?q?=E2=80=9CBig=E2=80=9D=20computer,=20wit?= =?UTF-8?q?h=20large,=20flat=20memory?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- alt/README.md | 19 +- alt/big.py | 174 ++++++++++++++ alt/compare.py | 3 + alt/test_big.py | 602 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 794 insertions(+), 4 deletions(-) create mode 100644 alt/big.py create mode 100755 alt/test_big.py diff --git a/alt/README.md b/alt/README.md index 59b5edb..3da6f15 100644 --- a/alt/README.md +++ b/alt/README.md @@ -8,16 +8,25 @@ See each module for instructions. ## Enhanced chips -Four alternative implementations use more or less chip hardware to make programs run faster, or to fit larger programs in ROM: +Four alternative implementations use more or less chip hardware to make programs run faster, or to +fit larger programs in ROM: -[alt/sp.py](sp.py) adds instructions for pushing/popping values to/from the stack, making programs more compact. +[alt/sp.py](sp.py) adds instructions for pushing/popping values to/from the stack, making programs +more compact. -[alt/threaded.py](threaded.py) adds lightweight CALL/RTN instructions, enabling a very compact "threaded interpreter" translation, which runs a little slower. +[alt/threaded.py](threaded.py) adds lightweight CALL/RTN instructions, enabling a very compact +"threaded interpreter" translation, which runs a little slower. -[alt/shift.py](shift.py) adds a "shiftr" instruction, and rewrites "push constant 16; call Math.divide" to use it instead; also a more efficient Math.multiply using shiftr. +[alt/shift.py](shift.py) adds a "shiftr" instruction, and rewrites +"push constant 16; call Math.divide" to use it instead; also a more efficient Math.multiply using shiftr. [alt/eight.py](eight.py) is, finally, a _smaller_ CPU, by using an 8-bit ALU and 2 cycles per instruction. +[alt/big.py](big.py) has a single, flat memory space, with maximum RAM and the ability to read +data from ROM (and code from RAM.) This is much more flexible and realistic, but adds a cycle to +fetch each instruction from the shared memory system. + + ## Enhanced compiler/translators These implementations all use the standard CPU, and try to generate more efficient code for it: @@ -39,10 +48,12 @@ replaces certain function calls with lower-overhead "reduced" alternatives. | [alt/threaded.py](threaded.py) | 1,549 (+23%) | 8,100 (-68%) | 49,600 (+20%) | 173,750 (+34%) | | [alt/shift.py](shift.py) | 1,311 (+4%) | 26,050 (+1%) | 19,800 (-52%) | _same_ | | [alt/eight.py](eight.py) | 1,032 (-18%) | _same_ | +100% | +100% | +| [alt/big.py](big.py) | 1,444 (+14%) | *TBD* | +100% | +100% | | [alt/lazy.py](lazy.py) | _same_ | 23,650 (-8%) | 37,300 (-10%) | 111,000 (-14%) | | [alt/reg.py](reg.py) | _same_ | 20,900 (-19%) | 19,150 (-54%) | 59,000 (-54%) | | [alt/reduce.py](reduce.py) | _same_ | 27,350 (+6.5%) | 20,300 (-51%) | _same_ | + **ROM Size** is the total number of instructions in ROM when Pong is compiled and translated from the Jack source. diff --git a/alt/big.py b/alt/big.py new file mode 100644 index 0000000..7f7a511 --- /dev/null +++ b/alt/big.py @@ -0,0 +1,174 @@ +#! python + +"""A computer with a single 16-bit address space for ROM and RAM. + +Uses the same ISA and assembler as the normal Hack CPU. + +Both instructions and data can be read from any address. + +Writes to ROM addresses are ignored. + +A small portion of the RAM is reserved for screen buffer and I/O: +- 1000 words to hold 80x25 8-bit characters +- Keyboard and TTY in the same 1024-word "page". +- 23 other words are available for future expansion. + +Layout considerations: +- ROM is only large enough to fit a scheme/basic/forth interpreter (8K?) +- Screen buffer and I/O: 1K +- RAM fills as much of the rest of 64K as possible +- The low-memory page is RAM, for convenient access +- The ROM lives in the 15-bit addressable range, so a code address can always be loaded in one cycle +- "negative" addresses are a uniform block of RAM, useful for heap + + +| Address Range | Size (words) | Storage | Contents | +| 0x0000–0x03FF | 1K | RAM | Temporaries, "registers", etc. | +| 0x0400–0x07FF | 1K | RAM | Screen buffer and I/O | +| 0x0800–0x7FFF | 30K | ROM | Code: boot/runtime; data | +| 0x8000–0xFFFF | 32K | RAM | Heap or other large blocks | + +Note: it's also possible to treat all negative values as addresses in a continous block of RAM +starting at 0x8000 = -32768. In fact, this range extends all the way to the bottom of the ROM +at 0x0800 = 2048. + +TODO: make the size of the ROM configurable, so you can trade off heap space vs runtime size. +""" + +from nand import chip, lazy, RAM, ROM, Input, Output, DFF +from project_01 import And, Or, Not +from project_03 import Mux, Mux16, Register, PC, ALU +import project_05 +from alt.threaded import Eq16 + + +SCREEN_BASE = 0x0400 +KEYBOARD_ADDR = 0x07FF +ROM_BASE = 0x0800 +HEAP_BASE = 0x8000 + +@chip +def FlatMemory(inputs, outputs): + """The same interface as MemorySystem, but also maps the ROM and extends address to the full 16 bits. + + Note: this will need some additional support in the "codegen" simulator, which otherwise implements + the standard MemoryStstem directly. + """ + + in_ = inputs.in_ + load = inputs.load + address = inputs.address + + # addresses 0x08-- through 0x7F-- are in the ROM: + # - high bits 00001–01111 (0000 1000 0000 0000 to 0111 1111 1111 1111) + # - or, 0... 1... .... .... + # - or, 1 <= (address >> 11) < 16 + is_rom = And(a=Not(in_=address[15]).out, + b=Or(a=Or(a=address[14], + b=address[13]).out, + b=Or(a=address[12], + b=address[11]).out).out).out + + is_io = Eq16(a=address, b=KEYBOARD_ADDR).out + + ram = RAM(16)(in_=in_, load=load, address=address) # Fully 64K; partially hidden by the ROM + rom = ROM(15)(address=address) # Based at 0 and sized to 32K, but partially hidden by RAM + # screen = RAM(10) # Separate RAM would make life easier for the harness? + keyboard = Input() + tty = Output(in_=in_, load=is_io) + + outputs.out = Mux16(sel=is_io, + a=Mux16(sel=is_rom, a=ram.out, b=rom.out).out, + b=keyboard.out).out + outputs.tty_ready = tty.ready + + +@chip +def IdlableCPU(inputs, outputs): + """Same as the standard CPU, + """ + + inM = inputs.inM # M value input (M = contents of RAM[A]) + instruction = inputs.instruction # Instruction for execution + reset = inputs.reset # Signals whether to re-start the current + # program (reset==1) or continue executing + # the current program (reset==0). + # Extra for extension: + idle = inputs.idle # When set, *don't* increment any state + + i, _, _, a, c5, c4, c3, c2, c1, c0, da, dd, dm, jlt, jeq, jgt = [instruction[j] for j in reversed(range(16))] + + not_i = Not(in_=i).out + + not_idle = Not(in_=idle).out + + alu = lazy() + a_reg = Register(in_=Mux16(a=instruction, b=alu.out, sel=i).out, + load=And(a=not_idle, b=Or(a=not_i, b=da).out).out) + d_reg = Register(in_=alu.out, + load=And(a=not_idle, b=And(a=i, b=dd).out).out) + jump_lt = And(a=alu.ng, b=jlt).out + jump_eq = And(a=alu.zr, b=jeq).out + jump_gt = And(a=And(a=Not(in_=alu.ng).out, b=Not(in_=alu.zr).out).out, b=jgt).out + jump = And(a=i, + b=Or(a=jump_lt, b=Or(a=jump_eq, b=jump_gt).out).out + ).out + pc = PC(in_=a_reg.out, load=jump, inc=not_idle, reset=reset) + alu.set(ALU(x=d_reg.out, y=Mux16(a=a_reg.out, b=inM, sel=a).out, + zx=c5, nx=c4, zy=c3, ny=c2, f=c1, no=c0)) + + + outputs.outM = alu.out # M value output + outputs.writeM = And(a=dm, b=i).out # Write to M? + outputs.addressM = a_reg.out # Address in data memory (of M) (latched) + outputs.pc = pc.out # address of next instruction (latched) + + +@chip +def BigComputer(inputs, outputs): + """A computer with the standard CPU, but mapping RAM, ROM, and I/O into the same large, flat + memory space. + + In every odd cycle, an instruction is read from memory and stored in an extra Register + + Note: on start/reset, instructions from address 0 are read. Since a zero-value instruction + just loads 0 into A, we effectively execute 2K no-op "@0" instructions before reaching the + first actual ROM address. + """ + + reset = inputs.reset + + # A DFF to split each pair of cycles into two halves: + # fetch is True in the first half-cycle, when the instruction is fetched from memory. + # execute is True in the second half-cycle, when any read/write operations are done. + # The DFF stores execute (= !fetch), so that we start in fetch. + half_cycle = lazy() + fetch = Not(in_=half_cycle.out).out + half_cycle.set(DFF(in_=Mux(a=fetch, b=0, sel=reset).out)) + execute = half_cycle.out + + cpu = lazy() + + mem = FlatMemory(in_=cpu.outM, + load=And(a=execute, b=cpu.writeM).out, + address=Mux16(a=cpu.pc, b=cpu.addressM, sel=execute).out) + + instr_reg = Register(in_=cpu.pc, load=fetch) + + # TODO: Set an instruction code that results in less computation during simulation, or is + # there a way to do that that's more realistic? Maybe just leave the instruction unchanged + # and disable all state updates; if the ALUs inputs don't change, it consumes no power? + # Does that apply to (vector) simulation? + cpu.set(IdlableCPU(inM=mem.out, + instruction=instr_reg.out, + reset=reset, + idle=fetch)) + + # HACK: need some dependency to force the whole thing to be synthesized. + # Exposing the PC also makes it easy to observe what's happening in a dumb way. + outputs.pc = cpu.pc + + # HACK: similar issues, but in this case it's just the particular component that + # needs to be forced to be included. + outputs.tty_ready = mem.tty_ready + diff --git a/alt/compare.py b/alt/compare.py index d704427..efac919 100755 --- a/alt/compare.py +++ b/alt/compare.py @@ -16,6 +16,7 @@ from alt.shift import SHIFT_PLATFORM from alt.sp import SP_PLATFORM from alt.threaded import THREADED_PLATFORM +import alt.big def main(): std = measure(BUNDLED_PLATFORM) @@ -34,6 +35,8 @@ def main(): # Note: currently the eight-bit CPU doesn't run correctly in the "codegen" simulator, so it's # a little painful to measure its performance directly. However, by design it takes exactly # two cycles per instruction, so we can just report that with a relatively clear conscience. + print_relative_result("alt/big.py", std, (gate_count(alt.big.BigComputer)['nands'], std[1], std[2]*2, std[3]*2)) # Cheeky + # Similar def print_result(name, t): diff --git a/alt/test_big.py b/alt/test_big.py new file mode 100755 index 0000000..063305f --- /dev/null +++ b/alt/test_big.py @@ -0,0 +1,602 @@ +#! /usr/bin/env pytest + +from nand import run, gate_count +import nand.component +from alt.big import * + + +def test_memory_system(): + # FIXME: not handled by "codegen" yet + mem = run(FlatMemory, simulator="vector") + + # set RAM[0] = -1 + mem.in_ = -1 + mem.load = 1 + mem.address = 0 + mem.tick(); mem.tock() + + # RAM[0] holds value + mem.in_ = 9999 + mem.load = 0 + mem.tick(); mem.tock() + assert mem.out == -1 + + # Did not also write to upper RAM or Screen (same lower bits) + mem.address = SCREEN_BASE + assert mem.out == 0 + mem.address = HEAP_BASE + assert mem.out == 0 + + # Set RAM[0x0200] = 2222 + mem.in_ = 2222 + mem.load = 1 + mem.address = 0x0200 + mem.tick(); mem.tock() + assert mem.out == 2222 + + # RAM[0x0200] holds value + mem.in_ = 9999 + mem.load = 0 + mem.tick(); mem.tock() + assert mem.out == 2222 + + # Did not also write to lower RAM or Screen + mem.address = 0 + assert mem.out == -1 + mem.address = SCREEN_BASE + assert mem.out == 0 + + # Low order address bits connected + # (note: not actually testing anything in this system?) + mem.address = 0x0001; assert mem.out == 0 + mem.address = 0x0002; assert mem.out == 0 + mem.address = 0x0004; assert mem.out == 0 + mem.address = 0x0008; assert mem.out == 0 + mem.address = 0x0010; assert mem.out == 0 + mem.address = 0x0020; assert mem.out == 0 + mem.address = 0x0040; assert mem.out == 0 + mem.address = 0x0080; assert mem.out == 0 + mem.address = 0x0100; assert mem.out == 0 + mem.address = 0x0200; assert mem.out == 2222 + mem.address = 0x0400; assert mem.out == 0 + # mem.address = 0x0800; assert mem.out == 0 + # mem.address = 0x1000; assert mem.out == 0 + # mem.address = 0x2000; assert mem.out == 0 + + # RAM[0x0123] = 1234 + mem.address = 0x0123 + mem.in_ = 1234 + mem.load = 1 + mem.tick(); mem.tock() + assert mem.out == 1234 + + # Did not also write to upper RAM or Screen + mem.address = SCREEN_BASE + 0x0123 + assert mem.out == 0 + mem.address = HEAP_BASE + 0x0123 + assert mem.out == 0 + + # RAM[0x0234] = 2345 + mem.address = 0x0234 + mem.in_ = 2345 + mem.load = 1 + mem.tick(); mem.tock() + assert mem.out == 2345 + + # Did not also write to lower RAM or Screen + mem.address = 0x0034 + assert mem.out == 0 + mem.address = 0x0434 + assert mem.out == 0 + + + ### Keyboard test + + ## Note: this test can't be done on the isolated MemorySystem, because the necessary + ## connections are only provided when the simulator detects the full Computer is being + ## simulated. Instead, we test it below. + # mem.address = 0x6000 + # assert mem.out == 75 + + + ### Screen test + + mem.load = 1 + mem.in_ = -1 + mem.address = SCREEN_BASE + 100 + mem.tick(); mem.tock() + assert mem.out == -1 + + # ...load still set + mem.address = SCREEN_BASE + 977 + mem.tick(); mem.tock() + assert mem.out == -1 + + +def test_rom(): + mem = run(FlatMemory, simulator="vector") + + # Writes to any ROM-mapped address are ignored: + for addr in (ROM_BASE, ROM_BASE + 1234, ROM_BASE + 29000, HEAP_BASE - 1): + mem.address = addr + mem.in_ = -1 + mem.load = 1 + mem.ticktock() + + assert mem.out == 0 + + + # Initialize with successive integers in the first ROM locations: + mem.init_rom([0]*ROM_BASE + list(range(100))) + + for i in range(100): + mem.address = ROM_BASE + i + assert mem.out == i + + # A write has no effect; you still read the ROM value + mem.address = ROM_BASE + 15 + mem.in_ = 123 + mem.load = 1 + mem.ticktock() + assert mem.out == 15 + + +def test_gates_mem(): + """Portion of the extra chip size that's in the memory system. + + This would have been extra chips on the board, not extra gates in the CPU, presumably. + """ + + assert gate_count(FlatMemory)['nands'] == 220 + + import project_05 + assert gate_count(project_05.MemorySystem)['nands'] == 163 + + +# def test_cpu(chip=project_05.CPU): +# cpu = run(chip) + +# cpu.instruction = 0b0011000000111001 # @12345 +# cpu.ticktock() +# assert cpu.writeM == 0 and cpu.addressM == 12345 and cpu.pc == 1 # and DRegister == 0 + +# cpu.instruction = 0b1110110000010000 # D=A +# cpu.ticktock() +# assert cpu.writeM == 0 and cpu.addressM == 12345 and cpu.pc == 2 # and DRegister == 12345 + +# cpu.instruction = 0b0101101110100000 # @23456 +# cpu.ticktock() +# assert cpu.writeM == 0 and cpu.addressM == 23456 and cpu.pc == 3 # and DRegister == 12345 + +# cpu.instruction = 0b1110000111010000 # D=A-D +# cpu.ticktock() +# assert cpu.writeM == 0 and cpu.addressM == 23456 and cpu.pc == 4 # and DRegister == 11111 + +# cpu.instruction = 0b0000001111101000 # @1000 +# cpu.ticktock() +# assert cpu.writeM == 0 and cpu.addressM == 1000 and cpu.pc == 5 # and DRegister == 11111 + +# cpu.instruction = 0b1110001100001000 # M=D +# cpu.ticktock() +# assert cpu.outM == 11111 and cpu.writeM == 1 and cpu.addressM == 1000 and cpu.pc == 6 # and DRegister == 11111 + +# cpu.instruction = 0b0000001111101001 # @1001 +# cpu.ticktock() +# assert cpu.writeM == 0 and cpu.addressM == 1001 and cpu.pc == 7 # and DRegister == 11111 + +# # Note confusing timing here: outM has the value to be written to memory when the clock falls. Afterward, +# # outM has a nonsense value. +# # TODO: always assert outM and writeM before tick/tock? +# cpu.instruction = 0b1110001110011000 # MD=D-1 +# assert cpu.outM == 11110 and cpu.writeM == 1 and cpu.addressM == 1001 and cpu.pc == 7 # and DRegister == 11111 +# cpu.ticktock() +# assert cpu.outM == 11109 and cpu.writeM == 1 and cpu.addressM == 1001 and cpu.pc == 8 # and DRegister == 11110 + +# cpu.instruction = 0b0000001111101000 # @1000 +# cpu.ticktock() +# assert cpu.writeM == 0 and cpu.addressM == 1000 and cpu.pc == 9 # and DRegister == 11110 + +# cpu.instruction = 0b1111010011010000 # D=D-M +# cpu.inM = 11111 +# cpu.ticktock() +# assert cpu.writeM == 0 and cpu.addressM == 1000 and cpu.pc == 10 # and DRegister == -1 + +# cpu.instruction = 0b0000000000001110 # @14 +# cpu.ticktock() +# assert cpu.writeM == 0 and cpu.addressM == 14 and cpu.pc == 11 # and DRegister == -1 + +# cpu.instruction = 0b1110001100000100 # D;jlt +# cpu.ticktock() +# assert cpu.writeM == 0 and cpu.addressM == 14 and cpu.pc == 14 # and DRegister == -1 + +# cpu.instruction = 0b0000001111100111 # @999 +# cpu.ticktock() +# assert cpu.writeM == 0 and cpu.addressM == 999 and cpu.pc == 15 # and DRegister == -1 + +# cpu.instruction = 0b1110110111100000 # A=A+1 +# cpu.ticktock() +# assert cpu.writeM == 0 and cpu.addressM == 1000 and cpu.pc == 16 # and DRegister == -1 + +# cpu.instruction = 0b1110001100001000 # M=D +# cpu.ticktock() +# assert cpu.outM == -1 and cpu.writeM == 1 and cpu.addressM == 1000 and cpu.pc == 17 # and DRegister == -1 + +# cpu.instruction = 0b0000000000010101 # @21 +# cpu.ticktock() +# assert cpu.writeM == 0 and cpu.addressM == 21 and cpu.pc == 18 # and DRegister == -1 + +# cpu.instruction = 0b1110011111000010 # D+1;jeq +# cpu.ticktock() +# assert cpu.writeM == 0 and cpu.addressM == 21 and cpu.pc == 21 # and DRegister == -1 + +# cpu.instruction = 0b0000000000000010 # @2 +# cpu.ticktock() +# assert cpu.writeM == 0 and cpu.addressM == 2 and cpu.pc == 22 # and DRegister == -1 + +# cpu.instruction = 0b1110000010010000 # D=D+A +# cpu.ticktock() +# assert cpu.writeM == 0 and cpu.addressM == 2 and cpu.pc == 23 # and DRegister == 1 + +# cpu.instruction = 0b0000001111101000 # @1000 +# cpu.ticktock() +# assert cpu.writeM == 0 and cpu.addressM == 1000 and cpu.pc == 24 # and DRegister == -1 + +# cpu.instruction = 0b1110111010010000 # D=-1 +# cpu.ticktock() +# assert cpu.writeM == 0 and cpu.addressM == 1000 and cpu.pc == 25 # and DRegister == -1 + +# cpu.instruction = 0b1110001100000001 # D;JGT +# cpu.ticktock() +# assert cpu.writeM == 0 and cpu.addressM == 1000 and cpu.pc == 26 # and DRegister == -1 + +# cpu.instruction = 0b1110001100000010 # D;JEQ +# cpu.ticktock() +# assert cpu.writeM == 0 and cpu.addressM == 1000 and cpu.pc == 27 # and DRegister == -1 + +# cpu.instruction = 0b1110001100000011 # D;JGE +# cpu.ticktock() +# assert cpu.writeM == 0 and cpu.addressM == 1000 and cpu.pc == 28 # and DRegister == -1 + +# cpu.instruction = 0b1110001100000100 # D;JLT +# cpu.ticktock() +# assert cpu.writeM == 0 and cpu.addressM == 1000 and cpu.pc == 1000 # and DRegister == -1 + +# cpu.instruction = 0b1110001100000101 # D;JNE +# cpu.ticktock() +# assert cpu.writeM == 0 and cpu.addressM == 1000 and cpu.pc == 1000 # and DRegister == -1 + +# cpu.instruction = 0b1110001100000110 # D;JLE +# cpu.ticktock() +# assert cpu.writeM == 0 and cpu.addressM == 1000 and cpu.pc == 1000 # and DRegister == -1 + +# cpu.instruction = 0b1110001100000111 # D;JMP +# cpu.ticktock() +# assert cpu.writeM == 0 and cpu.addressM == 1000 and cpu.pc == 1000 # and DRegister == -1 + +# cpu.instruction = 0b1110101010010000 # D=0 +# cpu.ticktock() +# assert cpu.writeM == 0 and cpu.addressM == 1000 and cpu.pc == 1001 # and DRegister == 0 + +# cpu.instruction = 0b1110001100000001 # D;JGT +# cpu.ticktock() +# assert cpu.writeM == 0 and cpu.addressM == 1000 and cpu.pc == 1002 # and DRegister == 0 + +# cpu.instruction = 0b1110001100000010 # D;JEQ +# cpu.ticktock() +# assert cpu.writeM == 0 and cpu.addressM == 1000 and cpu.pc == 1000 # and DRegister == 0 + +# cpu.instruction = 0b1110001100000011 # D;JGE +# cpu.ticktock() +# assert cpu.writeM == 0 and cpu.addressM == 1000 and cpu.pc == 1000 # and DRegister == 0 + +# cpu.instruction = 0b1110001100000100 # D;JLT +# cpu.ticktock() +# assert cpu.writeM == 0 and cpu.addressM == 1000 and cpu.pc == 1001 # and DRegister == 0 + +# cpu.instruction = 0b1110001100000101 # D;JNE +# cpu.ticktock() +# assert cpu.writeM == 0 and cpu.addressM == 1000 and cpu.pc == 1002 # and DRegister == 0 + +# cpu.instruction = 0b1110001100000110 # D;JLE +# cpu.ticktock() +# assert cpu.writeM == 0 and cpu.addressM == 1000 and cpu.pc == 1000 # and DRegister == 0 + +# cpu.instruction = 0b1110001100000111 # D;JMP +# cpu.ticktock() +# assert cpu.writeM == 0 and cpu.addressM == 1000 and cpu.pc == 1000 # and DRegister == 0 + +# cpu.instruction = 0b1110111111010000 # D=1 +# cpu.ticktock() +# assert cpu.writeM == 0 and cpu.addressM == 1000 and cpu.pc == 1001 # and DRegister == 1 + +# cpu.instruction = 0b1110001100000001 # D;JGT +# cpu.ticktock() +# assert cpu.writeM == 0 and cpu.addressM == 1000 and cpu.pc == 1000 # and DRegister == 1 + +# cpu.instruction = 0b1110001100000010 # D;JEQ +# cpu.ticktock() +# assert cpu.writeM == 0 and cpu.addressM == 1000 and cpu.pc == 1001 # and DRegister == 1 + +# cpu.instruction = 0b1110001100000011 # D;JGE +# cpu.ticktock() +# assert cpu.writeM == 0 and cpu.addressM == 1000 and cpu.pc == 1000 # and DRegister == 1 + +# cpu.instruction = 0b1110001100000100 # D;JLT +# cpu.ticktock() +# assert cpu.writeM == 0 and cpu.addressM == 1000 and cpu.pc == 1001 # and DRegister == 1 + +# cpu.instruction = 0b1110001100000101 # D;JNE +# cpu.ticktock() +# assert cpu.writeM == 0 and cpu.addressM == 1000 and cpu.pc == 1000 # and DRegister == 1 + +# cpu.instruction = 0b1110001100000110 # D;JLE +# cpu.ticktock() +# assert cpu.writeM == 0 and cpu.addressM == 1000 and cpu.pc == 1001 # and DRegister == 1 + +# cpu.instruction = 0b1110001100000111 # D;JMP +# cpu.ticktock() +# assert cpu.writeM == 0 and cpu.addressM == 1000 and cpu.pc == 1000 # and DRegister == 1 + +# cpu.reset = 1 +# cpu.ticktock() +# assert cpu.writeM == 0 and cpu.addressM == 1000 and cpu.pc == 0 # and DRegister == 1 + +# cpu.instruction = 0b0111111111111111 # @32767 +# cpu.reset = 0 +# cpu.ticktock() +# assert cpu.writeM == 0 and cpu.addressM == 32767 and cpu.pc == 1 # and DRegister == 1 + + +def test_gates_cpu(): + """Portion of the extra chip size that's in the CPU's "idle" state.""" + + assert gate_count(IdlableCPU)['nands'] == 1104 + + import project_05 + assert gate_count(project_05.CPU)['nands'] == 1099 + + +def test_computer_no_program(): + computer = run(BigComputer) + + for _ in range(100): + computer.ticktock() + + # Two-cycles per instruction: + assert computer.pc == 50 + + +# # Add.hack: +# ADD_PROGRAM = [ +# 0b0000000000000010, # @2 +# 0b1110110000010000, # D=A +# 0b0000000000000011, # @3 +# 0b1110000010010000, # D=D+A +# 0b0000000000000001, # @1 Note: modified to avoid address 0 (SP), which may get special treatment +# 0b1110001100001000, # M=D +# ] + +# def test_computer_add(chip=project_05.Computer, simulator="vector"): +# computer = run(chip, simulator=simulator) + +# # First run (at the beginning PC=0) +# computer.run_program(ADD_PROGRAM) + +# assert computer.peek(1) == 5 + + +# # Reset the PC +# computer.reset = 1 +# computer.ticktock() +# assert computer.pc == 0 + +# # Second run, to check that the PC was reset correctly. +# computer.poke(1, 12345) +# computer.reset = 0 +# while computer.pc < len(ADD_PROGRAM): +# computer.ticktock() + +# assert computer.peek(1) == 5 + + +# MAX_PROGRAM = [ +# # Note: modified to avoid address 0 (SP), which may get special treatment +# 0b0000000000000001, # 0: @1 +# 0b1111110000010000, # 1: D=M +# 0b0000000000000010, # 2: @2 +# 0b1111010011010000, # 3: D=D-M ; D = mem[1] - mem[2] +# 0b0000000000001010, # 4: @10 +# 0b1110001100000001, # 5: D; JGT +# 0b0000000000000010, # 6: @2 +# 0b1111110000010000, # 7: D=M ; D = mem[2] +# 0b0000000000001100, # 8: @12 +# 0b1110101010000111, # 9: JMP +# 0b0000000000000001, # 10: @1 +# 0b1111110000010000, # 11: D=M ; D = mem[1] +# 0b0000000000000011, # 12: @3 +# 0b1110001100001000, # 13: M=D ; mem[3] = max +# 0b0000000000001110, # 14: @14 +# 0b1110101010000111, # 15: JMP ; infinite loop +# ] + +# def test_computer_max(chip=project_05.Computer, simulator="vector", cycles_per_instr=1): +# computer = run(chip, simulator=simulator) + +# computer.init_rom(MAX_PROGRAM) + +# # first run: compute max(3,5) +# computer.poke(1, 3) +# computer.poke(2, 5) +# for _ in range(14*cycles_per_instr): +# computer.ticktock() +# assert computer.peek(3) == 5 + +# # second run: compute max(23456,12345) +# computer.reset_program() +# computer.poke(1, 23456) +# computer.poke(2, 12345) +# # The run on these inputs needs less cycles (different branching) +# for _ in range(10*cycles_per_instr): +# computer.ticktock() +# assert computer.peek(3) == 23456 + +# # Copy one keycode value from the address where the keyboard is mapped to the RAM. +# COPY_INPUT_PROGRAM = [ +# 24576, # @(0x6000) +# 64528, # D=M (D = keycode) +# 1, # @1 +# 58120, # M=D (mem[1] = D) +# 4, # @4 +# 60039, # 0;JMP (infinite loop) +# ] + +# def test_computer_keyboard(chip=project_05.Computer, simulator="vector", cycles_per_instr=1): +# """A value which is presented via a special `Input` component can be read from the +# address 0x6000, where the "keyboard" is mapped. + +# Note: can't test this at the level of MemorySystem, because the wrapper for the full +# computer provides some of the necessary plumbing. +# """ + +# computer = run(chip, simulator=simulator) + +# computer.init_rom(COPY_INPUT_PROGRAM) + +# KEY_A = ord("a") + +# computer.set_keydown(KEY_A) +# for _ in range(4*cycles_per_instr): +# computer.ticktock() + +# assert computer.peek(1) == KEY_A + + +# 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 + + +# # Write a few constant values to the external "tty" interface: +# WRITE_TTY_PROGRAM = [ +# 1, # @1 +# 60432, # D=A +# 24576, # @(0x6000) +# 58120, # M=D (write 1) + +# 0, # @0 +# 60432, # D=A +# 24576, # @(0x6000) +# 58120, # M=D ("write" 0; no effect) + +# 12345, # @12345 +# 60432, # D=A +# 24576, # @(0x6000) +# 58120, # M=D (write 12345) + +# 12, # @12 +# 60039, # 0; JMP (infinite loop) +# ] + +# def test_computer_tty(chip=project_05.Computer, simulator="vector", cycles_per_instr=1): +# """A value which is written to the address 0x6000 can be read from outside via +# a special `Output` component. + +# Also, the presence of a value in that component is signalled by the `tty_ready` + +# Note: can't test this at the level of MemorySystem, because the wrapper for the full +# computer provides some of the necessary plumbing. +# """ + +# computer = run(chip, simulator=simulator) + +# computer.init_rom(WRITE_TTY_PROGRAM) + +# # Run until a value appears (after 4 instructions): + +# cycles = 0 +# while computer.tty_ready and cycles < 1000: +# computer.ticktock() +# cycles += 1 + +# print(f"cycles: {cycles}") +# assert computer.tty_ready == False +# assert computer.get_tty() == 1 +# assert computer.tty_ready == True +# assert cycles == 4*cycles_per_instr # Bogus? + + +# # Now run four more instructions; nothing written this time: + +# for _ in range(4*cycles_per_instr): +# computer.ticktock() + +# assert computer.tty_ready == True +# assert computer.get_tty() == 0 +# assert computer.tty_ready == True + + +# # One more time, with a different value: + +# cycles = 0 +# while computer.tty_ready and cycles < 1000: +# computer.ticktock() +# cycles += 1 + +# assert computer.tty_ready == False +# assert computer.get_tty() == 12345 +# assert computer.tty_ready == True +# assert cycles == 4*cycles_per_instr # Bogus? + + + +# 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_speed(chip=project_05.Computer, cycles_per_instr=1): +# cps = cycles_per_second(chip, cycles_per_instr) +# print(f"Measured speed: {cps:0,.1f} cycles/s") +# assert cps > 500 # Note: about 1k/s is expected, but include a wide margin for random slowness + + +def test_gates_computer(): + """Overall extra chip size.""" + + assert gate_count(BigComputer)['nands'] == 1444 + + import project_05 + assert gate_count(project_05.Computer)['nands'] == 1262 + From 1f3be94bb906a4f90931629170face51da3566b8 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Thu, 18 Jan 2024 10:43:19 -0500 Subject: [PATCH 003/221] Comments --- alt/big.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/alt/big.py b/alt/big.py index 7f7a511..68e4f8e 100644 --- a/alt/big.py +++ b/alt/big.py @@ -85,16 +85,15 @@ def FlatMemory(inputs, outputs): @chip def IdlableCPU(inputs, outputs): - """Same as the standard CPU, - """ + """Same as the standard CPU, plus an 'idle' input that suspends all state updates.""" inM = inputs.inM # M value input (M = contents of RAM[A]) instruction = inputs.instruction # Instruction for execution reset = inputs.reset # Signals whether to re-start the current # program (reset==1) or continue executing # the current program (reset==0). - # Extra for extension: - idle = inputs.idle # When set, *don't* increment any state + # Extra for fetch/execute cycles: + idle = inputs.idle # When set, *don't* update any state i, _, _, a, c5, c4, c3, c2, c1, c0, da, dd, dm, jlt, jeq, jgt = [instruction[j] for j in reversed(range(16))] @@ -129,11 +128,18 @@ def BigComputer(inputs, outputs): """A computer with the standard CPU, but mapping RAM, ROM, and I/O into the same large, flat memory space. - In every odd cycle, an instruction is read from memory and stored in an extra Register + In every even (fetch) cycle, an instruction is read from memory and stored in an extra Register. + In odd (execute) cycles, memory and cpu state are updated as required by the instruction. Note: on start/reset, instructions from address 0 are read. Since a zero-value instruction just loads 0 into A, we effectively execute 2K no-op "@0" instructions before reaching the first actual ROM address. + + TODO: some instructions don't require access to the memory; they don't read or write from/to M. + In that case, we could fetch and execute in a single cycle if the CPU exposed that info or took + over control of the cycles. It looks like possibly as much as 50% of all instructions could + execute in one cycle for 25% speedup. On the other hand, it would complicate tests somewhat + unless we add "performance counters" to the CPU to keep track. """ reset = inputs.reset From 312c34703f1ed72c781c3ee5defabbf634e711ac Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sat, 20 Jan 2024 19:52:22 -0500 Subject: [PATCH 004/221] Actually parse hex literals in @-instructions --- nand/solutions/solved_06.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nand/solutions/solved_06.py b/nand/solutions/solved_06.py index fa68834..050f861 100644 --- a/nand/solutions/solved_06.py +++ b/nand/solutions/solved_06.py @@ -70,7 +70,7 @@ def parse_op(string, symbols=None): but included in the signature in so that other compatible parsers can use it. """ - m = re.match(r"@((?:0x)?\d+)", string) + m = re.match(r"@((0x[0-9a-fA-F]+)|([1-9][0-9]*)|0)", string) if m: return eval(m.group(1)) else: From 4775886471d2a6a2ff911704be2e163a7511d719 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sat, 20 Jan 2024 21:50:56 -0500 Subject: [PATCH 005/221] First example for big.py --- alt/big.py | 155 +++++++++++- alt/big/Monaco9.png | Bin 0 -> 862 bytes alt/big/Output.asm | 460 ++++++++++++++++++++++++++++++++++++ nand/solutions/solved_06.py | 6 +- 4 files changed, 614 insertions(+), 7 deletions(-) mode change 100644 => 100755 alt/big.py create mode 100644 alt/big/Monaco9.png create mode 100644 alt/big/Output.asm diff --git a/alt/big.py b/alt/big.py old mode 100644 new mode 100755 index 68e4f8e..6624917 --- a/alt/big.py +++ b/alt/big.py @@ -1,4 +1,4 @@ -#! python +#! /usr/bin/env python """A computer with a single 16-bit address space for ROM and RAM. @@ -33,14 +33,21 @@ at 0x0800 = 2048. TODO: make the size of the ROM configurable, so you can trade off heap space vs runtime size. + +For authentic Macintosh fonts, see https://archive.org/details/AppleMacintoshSystem753 """ from nand import chip, lazy, RAM, ROM, Input, Output, DFF +import nand.syntax from project_01 import And, Or, Not from project_03 import Mux, Mux16, Register, PC, ALU import project_05 +from nand.solutions import solved_06 from alt.threaded import Eq16 +import computer +import pygame.image + SCREEN_BASE = 0x0400 KEYBOARD_ADDR = 0x07FF @@ -112,7 +119,7 @@ def IdlableCPU(inputs, outputs): jump = And(a=i, b=Or(a=jump_lt, b=Or(a=jump_eq, b=jump_gt).out).out ).out - pc = PC(in_=a_reg.out, load=jump, inc=not_idle, reset=reset) + pc = PC(in_=a_reg.out, load=And(a=not_idle, b=jump).out, inc=not_idle, reset=reset) alu.set(ALU(x=d_reg.out, y=Mux16(a=a_reg.out, b=inM, sel=a).out, zx=c5, nx=c4, zy=c3, ny=c2, f=c1, no=c0)) @@ -155,11 +162,13 @@ def BigComputer(inputs, outputs): cpu = lazy() + addr = Mux16(a=cpu.pc, b=cpu.addressM, sel=execute).out + mem = FlatMemory(in_=cpu.outM, load=And(a=execute, b=cpu.writeM).out, - address=Mux16(a=cpu.pc, b=cpu.addressM, sel=execute).out) + address=addr) - instr_reg = Register(in_=cpu.pc, load=fetch) + instr_reg = Register(in_=mem.out, load=fetch) # TODO: Set an instruction code that results in less computation during simulation, or is # there a way to do that that's more realistic? Maybe just leave the instruction unchanged @@ -178,3 +187,141 @@ def BigComputer(inputs, outputs): # needs to be forced to be included. outputs.tty_ready = mem.tty_ready + # TEMP: + outputs.fetch = fetch + # outputs.execute = execute + outputs.addr = addr + # outputs.addressM = cpu.addressM + outputs.writeM = cpu.writeM + outputs.outM = cpu.outM + outputs.instr = instr_reg.out + + +class TextKVM(computer.KVM): + """Keyboard and display, displaying characters using a set of baked-in glyphs. + + Each word of the screen buffer stores a pair of 8-bit characters. + """ + + def __init__(self, title, char_width, char_height, glyph_width, glyph_height, bitmap_path): + computer.KVM.__init__(self, title, char_width*glyph_width, char_height*glyph_height) + + self.char_width = char_width + self.char_height = char_height + self.glyph_width = glyph_width + self.glyph_height = glyph_height + + self.glyph_sheet = pygame.image.load(bitmap_path) + + + def update_display(self, get_chars): + self.screen.fill(computer.COLORS[0]) + + stride = self.char_width//2 + for y in range(0, self.char_height): + for x in range(0, self.char_width, 2): + pair = get_chars(y*stride + x//2) + self.render( x, y, pair & 0xFF) + self.render(x+1, y, pair >> 8) + + pygame.display.flip() + + + def render(self, x, y, c): + g_x = (c & 0x0F)*self.glyph_width + g_y = (c >> 4)*self.glyph_height + self.screen.blit(self.glyph_sheet, + dest=(x*self.glyph_width, + y*self.glyph_height), + area=pygame.Rect(g_x, + g_y, + self.glyph_width, + self.glyph_height)) + + + + + +def run(chip, program, name="Flat!", font="monaco-9", halt_addr=None): + """Run with keyboard and text-mode graphics.""" + + # TODO: font + + computer = nand.syntax.run(chip, simulator="vector") + # print(dir(computer)) + + computer.init_rom(program) + + # Jump over low memory that we might be using for debugging: + computer.poke(0, solved_06.parse_op(f"@{ROM_BASE}")) + computer.poke(1, solved_06.parse_op("0;JMP")) + # print(f"ROM={computer._rom.storage[ROM_BASE:ROM_BASE+10]}") + + # TEMP: splat some chars into the screen ROM + # computer.poke(SCREEN_BASE, 0x41 + 25) + # computer.poke(SCREEN_BASE+40+1, 0x6162) + # computer.poke(SCREEN_BASE+999, 0x40) + + # print(f"{computer._stateful}") + + kvm = TextKVM(name, 80, 25, 6, 10, "alt/big/Monaco9.png") + + # TODO: use computer.py's "run", for many more features + cycles = 0 + halted = False + while True: + if not halted and computer.pc == halt_addr: + halted = True + print(f"halted after {cycles} cycles") + + # if not halted: + # print(f"@{computer.pc}; outputs: {computer.outputs()}") + # print(f" R0: {computer.peek(0)}; R1: {computer.peek(1)}") + + # computer.ticktock(100) + # cycles += 100 + computer.ticktock() + cycles += 1 + + + if cycles % 100 == 0: + key = kvm.process_events() + kvm.update_display(lambda x: computer.peek(SCREEN_BASE + x)) + + msgs = [ + f"{cycles/1000:0.1f}k cycles", + f"@{computer.pc}", + f"MEM[SCREEN_BASE]={computer.peek(SCREEN_BASE)}" + ] + pygame.display.set_caption(f"{name}: {'; '.join(msgs)}") + + +def main(): + import argparse + parser = argparse.ArgumentParser(description="Run assembly source with text-mode display and keyboard") + parser.add_argument("path", help="Path to source (.asm)") + # parser.add_argument("--simulator", action="store", default="codegen", help="One of 'vector' (slower, more precise); 'codegen' (faster, default); 'compiled' (experimental)") + # parser.add_argument("--trace", action="store_true", help="(VM/Jack-only) print cycle counts during initialization. Note: runs almost 3x slower.") + # parser.add_argument("--print", action="store_true", help="(VM/Jack-only) print translated assembly.") + # TODO: "--debug" showing opcode-level trace. Breakpoints, stepping, peek/poke? + # parser.add_argument("--no-waiting", action="store_true", help="(VM/Jack-only) substitute a no-op function for Sys.wait.") + # parser.add_argument("--max-fps", action="store", type=int, help="Experimental! (VM/Jack-only) pin the game loop to a fixed rate, approximately (in games that use Sys.wait).\nMay or may not work, depending on the translator.") + # TODO: "--max-cps"; limit the clock speed directly. That will allow different chips to be compared (in a way). + # TODO: "--headless" with no UI, with Keyboard and TTY connected to stdin/stdout + + args = parser.parse_args() + + print(f"Reading assembly from file: {args.path}") + with open(args.path, mode='r') as f: + prg, symbols, statics = solved_06.assemble(f, start_addr=ROM_BASE) + + + print(f"Size in ROM: {len(prg) - ROM_BASE:0,d}") + print(f"symbols: {symbols}") + print(f"statics: {statics}") + + run(chip=BigComputer, program=prg, halt_addr=symbols["halt"]) + + +if __name__ == "__main__": + main() diff --git a/alt/big/Monaco9.png b/alt/big/Monaco9.png new file mode 100644 index 0000000000000000000000000000000000000000..c7663304ebe0717bcf827760a041973f46c990ac GIT binary patch literal 862 zcmeAS@N?(olHy`uVBq!ia0vp^2|&DngBeI3SvPYZkYX$ja(7}_cTVOdkdqML6XFV_ zl@v@|HEhx>4g15a?-#1wT#)we@T}jT%w|3f$l7OMJ|)jPQ$NUC#mL6e-oC^jp(#4= zVe;(%@n@f$zWwL_|NpbsZf~h;&&RMxDbTd5pdiAdU>0Vv=2EUW{oCn&W zP!i-9%m7r44@{c1wv2&+sln65F(jk$Z3JV{VFM21|Np<_tu+)%Fps@`H%~pc`0f!m z2NqAWHU*9*1&(@|Z;Zudzh^8ydvTxpf;Ep-)te9Gx&1uue^^es?w!MA&YCQNGqawT zJ_~%uxn}O{<*b*kuKO%M+h*f;p9g&BYg}h#vGA{J$=$!8zJNF5;jxR8&z5|f_jl!r zLjmT~Yi@k|BKq&x;&5J@?ElvrqT;T;wp*AIx$oPJWbt2Hwmi#D``Q#0b(pR4*|cP? z)q85rHZ4;=QG1fR_ZO$&8K1Sm<;4?BvYPh(@7(dLb@Wo}8Tjik^H{{jfRi%dWronVwuZRddg7<2?6uvjv=lTBp8xd;DT>}b@2=c(jq~dcUiz|^ ? (48) +// 40 @ A B C D E F G H I J K L M N O (64) +// 50 P Q R S T U V W X Y Z [ / ] ^ _ (80) +// 60 ` a b c d e f g h i j k l m n o (96) +// 70 p q r s t u v w x y z { | } ~ (112) +// +// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +// +// C D + +// Print "A", "B", "C", "D in the four corners: +@65 +D=A +@1024 +M=D +@16896 +D=A +@1063 +M=D +@67 +D=A +@1984 +M=D +@17408 +D=A +@2023 +M=D + +// TODO: copy from @table to @SCREEN_BASE + 2*80 +(copy_start) +// R0 = start of third row of screen buffer +@1104 +D=A +@R0 +M=D +// R1 = @table_start (start of the table in ROM) +@table_start +D=A +@R1 +M=D + +(copy_loop) +// D = MEM[R1++] +@R1 +A=M +D=M +//@R2 +//M=D +// R1 = R1+1 +@R1 +M=M+1 +// MEM[R0++] = D +@R0 +A=M +M=D +@R0 +M=M+1 + +// Check for end of table: +@R1 +D=M +@table_end +D=D-A +@copy_loop +D;JLE + + +(halt) +@halt +0;JMP + +// 40 words per line, for a header and then 8 lines for the lower half of the 8-bit table: +(table_start) +// header +@0 +@0 +@0 +@0 +@0x3030 +@0 +@0x3130 +@0 +@0x3230 +@0 +@0x3330 +@0 +@0x3430 +@0 +@0x3530 +@0 +@0x3630 +@0 +@0x3730 +@0 +@0x3830 +@0 +@0x3930 +@0 +@0x4130 +@0 +@0x4230 +@0 +@0x4330 +@0 +@0x4430 +@0 +@0x4530 +@0 +@0x4630 +@0 +@0 +@0 +@0 +@0 + +// 0x00–0F: +@0 +@0 +@0x3030 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 + +// 0x10–1F: +@0 +@0 +@0x3031 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 +@0 + +// 0x20–2F: +@0 +@0 +@0x3032 +@0 +@0x0020 +@0 +@0x0021 +@0 +@0x0022 +@0 +@0x0023 +@0 +@0x0024 +@0 +@0x0025 +@0 +@0x0026 +@0 +@0x0027 +@0 +@0x0028 +@0 +@0x0029 +@0 +@0x002A +@0 +@0x002B +@0 +@0x002C +@0 +@0x002D +@0 +@0x002E +@0 +@0x002F +@0 +@0 +@0 +@0 +@0 + +// 0x30–3F: +@0 +@0 +@0x3033 +@0 +@0x0030 +@0 +@0x0031 +@0 +@0x0032 +@0 +@0x0033 +@0 +@0x0034 +@0 +@0x0035 +@0 +@0x0036 +@0 +@0x0037 +@0 +@0x0038 +@0 +@0x0039 +@0 +@0x003A +@0 +@0x003B +@0 +@0x003C +@0 +@0x003D +@0 +@0x003E +@0 +@0x003F +@0 +@0 +@0 +@0 +@0 + +// 0x40–4F: +@0 +@0 +@0x3034 +@0 +@0x0040 +@0 +@0x0041 +@0 +@0x0042 +@0 +@0x0043 +@0 +@0x0044 +@0 +@0x0045 +@0 +@0x0046 +@0 +@0x0047 +@0 +@0x0048 +@0 +@0x0049 +@0 +@0x004A +@0 +@0x004B +@0 +@0x004C +@0 +@0x004D +@0 +@0x004E +@0 +@0x004F +@0 +@0 +@0 +@0 +@0 + +// 0x50–5F: +@0 +@0 +@0x3035 +@0 +@0x0050 +@0 +@0x0051 +@0 +@0x0052 +@0 +@0x0053 +@0 +@0x0054 +@0 +@0x0055 +@0 +@0x0056 +@0 +@0x0057 +@0 +@0x0058 +@0 +@0x0059 +@0 +@0x005A +@0 +@0x005B +@0 +@0x005C +@0 +@0x005D +@0 +@0x005E +@0 +@0x005F +@0 +@0 +@0 +@0 +@0 + +// 0x60–6F: +@0 +@0 +@0x3036 +@0 +@0x0060 +@0 +@0x0061 +@0 +@0x0062 +@0 +@0x0063 +@0 +@0x0064 +@0 +@0x0065 +@0 +@0x0066 +@0 +@0x0067 +@0 +@0x0068 +@0 +@0x0069 +@0 +@0x006A +@0 +@0x006B +@0 +@0x006C +@0 +@0x006D +@0 +@0x006E +@0 +@0x006F +@0 +@0 +@0 +@0 +@0 + +// 0x70–7F: +@0 +@0 +@0x3037 +@0 +@0x0070 +@0 +@0x0071 +@0 +@0x0072 +@0 +@0x0073 +@0 +@0x0074 +@0 +@0x0075 +@0 +@0x0076 +@0 +@0x0077 +@0 +@0x0078 +@0 +@0x0079 +@0 +@0x007A +@0 +@0x007B +@0 +@0x007C +@0 +@0x007D +@0 +@0x007E +@0 +@0x007F +@0 +@0 +@0 +@0 +@0 + +(table_end) diff --git a/nand/solutions/solved_06.py b/nand/solutions/solved_06.py index 050f861..cd3fbf0 100644 --- a/nand/solutions/solved_06.py +++ b/nand/solutions/solved_06.py @@ -105,7 +105,7 @@ def parse_op(string, symbols=None): raise Exception(f"unrecognized: {string}") -def assemble(lines, parse_op=parse_op, min_static=16, max_static=255): +def assemble(lines, parse_op=parse_op, min_static=16, max_static=255, start_addr=0): """Parse a sequence of lines them as assembly commands, accounting for builtin symbols, labels, and variables. @@ -132,7 +132,7 @@ def assemble(lines, parse_op=parse_op, min_static=16, max_static=255): # Second pass: resolve labels to locations symbols = {} - loc = 0 + loc = start_addr for line in code_lines: m = re.match(r"\((.*)\)", line) if m: @@ -148,7 +148,7 @@ def assemble(lines, parse_op=parse_op, min_static=16, max_static=255): loc += 1 # Third pass: parse all other instructions, and resolve non-label symbols (i.e. "static" allocations.) - ops = [] + ops = [0]*start_addr statics = {} next_static = min_static for line in code_lines: From 3433e5cfe79618e30e07139a15a465c6c84186fb Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 21 Jan 2024 09:39:31 -0500 Subject: [PATCH 006/221] More options for symbols and statics BUILTIN_SYMBOLS can be overridden. If min_statics is None, any use of an undeclared symbol is a ParseError. Range-checking for constant values. A ParseError type for slightly more clarity about where the errors are coming from. --- nand/solutions/solved_06.py | 49 +++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/nand/solutions/solved_06.py b/nand/solutions/solved_06.py index cd3fbf0..f7d6e55 100644 --- a/nand/solutions/solved_06.py +++ b/nand/solutions/solved_06.py @@ -45,6 +45,11 @@ "MP": 0b111, } +def register_names(count): + """Names for the first locations in low memory: R0, R1, ...""" + return { f"R{i}": i for i in range(count) } + + BUILTIN_SYMBOLS = { **{ "SP": 0, @@ -55,7 +60,7 @@ "SCREEN": 0x4000, "KEYBOARD": 0x6000, }, - **{ f"R{i}": i for i in range(16)} + **register_names(16) } @@ -68,11 +73,17 @@ def parse_op(string, symbols=None): :param symbols: a dictionary mapping symbol names to addresses (of labels in the code and memory locations allocated for "static" variables.) Note: not used in this implementation, but included in the signature in so that other compatible parsers can use it. + + Extra syntax: + - Constant values may be specified in hex, e.g. @0x5555 """ m = re.match(r"@((0x[0-9a-fA-F]+)|([1-9][0-9]*)|0)", string) if m: - return eval(m.group(1)) + value = eval(m.group(1)) + if value < 0 or value > 0x7FFF: + raise ParseError(f"A-command value out of range: {value}") + return value else: m = re.match(r"(?:([ADM]+)=)?([^;]+)(?:;J(..))?", string) if m: @@ -90,7 +101,7 @@ def parse_op(string, symbols=None): alu = ALU_CONTROL[alu_str] m_for_a = int('M' in m.group(2)) else: - raise Exception(f"unrecognized alu op: {m.group(2)}") + raise ParseError(f"unrecognized alu op: {m.group(2)}") jmp_str = m.group(3) if jmp_str is None: @@ -98,14 +109,14 @@ def parse_op(string, symbols=None): elif jmp_str in JMP_CONTROL: jmp = JMP_CONTROL[jmp_str] else: - raise Exception(f"unrecognized jump: J{m.group(3)}") + raise ParseError(f"unrecognized jump: J{m.group(3)}") return (0b111 << 13) | (m_for_a << 12) | (alu << 6) | (dest << 3) | jmp else: - raise Exception(f"unrecognized: {string}") + raise ParseError(f"unrecognized: {string}") -def assemble(lines, parse_op=parse_op, min_static=16, max_static=255, start_addr=0): +def assemble(lines, parse_op=parse_op, min_static=16, max_static=255, start_addr=0, builtins=BUILTIN_SYMBOLS): """Parse a sequence of lines them as assembly commands, accounting for builtin symbols, labels, and variables. @@ -137,8 +148,8 @@ def assemble(lines, parse_op=parse_op, min_static=16, max_static=255, start_addr m = re.match(r"\((.*)\)", line) if m: name = m.group(1) - if name in BUILTIN_SYMBOLS: - raise Exception(f"Attempt to redefine builtin symbol {name} at location {loc}") + if name in builtins: + raise ParseError(f"Attempt to redefine builtin symbol {name} at location {loc}") elif name in symbols: # This isn't an error because allowing re-definition makes it easy to hackishly # override something (see alt/shift.py). Sorry, world. @@ -158,19 +169,27 @@ def assemble(lines, parse_op=parse_op, min_static=16, max_static=255, start_addr m = re.match(r"@(\D.*)", line) if m: name = m.group(1) - if name in BUILTIN_SYMBOLS: - ops.append(BUILTIN_SYMBOLS[name]) + if name in builtins: + ops.append(builtins[name]) elif name in symbols: ops.append(symbols[name]) elif name in statics: ops.append(statics[name]) else: - if next_static > max_static: - raise Exception(f"Unable to allocate static storage for symbol {name}; already used all {max_static - min_static + 1} available locations") - statics[name] = next_static - ops.append(next_static) - next_static += 1 + if next_static is None: + raise ParseError(f"Unable to allocate static storage for symbol {name}; no static allocation space available") + elif next_static > max_static: + raise ParseError(f"Unable to allocate static storage for symbol {name}; already used all {max_static - min_static + 1} available locations") + else: + statics[name] = next_static + ops.append(next_static) + next_static += 1 else: ops.append(parse_op(line, symbols)) return (ops, symbols, statics) + + +class ParseError(Exception): + def __init__(self, msg): + Exception.__init__(self, msg) From d2fd7fe2fa9b46230deb37ffb7ba25709fab9dcb Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 21 Jan 2024 09:42:28 -0500 Subject: [PATCH 007/221] Cleanup --- alt/big/Output.asm | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/alt/big/Output.asm b/alt/big/Output.asm index 6077113..f8fcf1b 100644 --- a/alt/big/Output.asm +++ b/alt/big/Output.asm @@ -3,16 +3,16 @@ // A B // // 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F -// 00 (0) -// 10 (16) -// 20 ! " # $ % & ' ( ) * + , - . / (32) -// 30 0 1 2 3 4 5 6 7 8 9 : ; < = > ? (48) -// 40 @ A B C D E F G H I J K L M N O (64) -// 50 P Q R S T U V W X Y Z [ / ] ^ _ (80) -// 60 ` a b c d e f g h i j k l m n o (96) -// 70 p q r s t u v w x y z { | } ~ (112) +// 00 +// 10 +// 20 ! " # $ % & ' ( ) * + , - . / +// 30 0 1 2 3 4 5 6 7 8 9 : ; < = > ? +// 40 @ A B C D E F G H I J K L M N O +// 50 P Q R S T U V W X Y Z [ / ] ^ _ +// 60 ` a b c d e f g h i j k l m n o +// 70 p q r s t u v w x y z { | } ~ // -// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +// ... // // C D @@ -34,7 +34,6 @@ D=A @2023 M=D -// TODO: copy from @table to @SCREEN_BASE + 2*80 (copy_start) // R0 = start of third row of screen buffer @1104 @@ -52,9 +51,6 @@ M=D @R1 A=M D=M -//@R2 -//M=D -// R1 = R1+1 @R1 M=M+1 // MEM[R0++] = D From 0ab953d98a39625fa7b7d203cd9741c04bfe7d66 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 21 Jan 2024 09:42:45 -0500 Subject: [PATCH 008/221] Use new SCREEN builtin --- alt/big/Output.asm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alt/big/Output.asm b/alt/big/Output.asm index f8fcf1b..2548dd4 100644 --- a/alt/big/Output.asm +++ b/alt/big/Output.asm @@ -19,7 +19,7 @@ // Print "A", "B", "C", "D in the four corners: @65 D=A -@1024 +@SCREEN M=D @16896 D=A From 8a665afb9e3381678a60bc6149e1bb9449ffc7ff Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 21 Jan 2024 09:45:36 -0500 Subject: [PATCH 009/221] Use an old trick to take 2 instructions out of each loop iteration --- alt/big/Output.asm | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/alt/big/Output.asm b/alt/big/Output.asm index 2548dd4..38773dd 100644 --- a/alt/big/Output.asm +++ b/alt/big/Output.asm @@ -49,16 +49,14 @@ M=D (copy_loop) // D = MEM[R1++] @R1 -A=M -D=M -@R1 M=M+1 +A=M-1 +D=M // MEM[R0++] = D @R0 -A=M -M=D -@R0 M=M+1 +A=M-1 +M=D // Check for end of table: @R1 From 319123da319993410977a774b2aabdbfc6f12d62 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 21 Jan 2024 09:45:59 -0500 Subject: [PATCH 010/221] Use new assembler features --- alt/big.py | 53 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/alt/big.py b/alt/big.py index 6624917..72f33a3 100755 --- a/alt/big.py +++ b/alt/big.py @@ -47,6 +47,7 @@ import computer import pygame.image +import time SCREEN_BASE = 0x0400 @@ -239,30 +240,41 @@ def render(self, x, y, c): self.glyph_height)) +BUILTIN_SYMBOLS = { + **{ + "SCREEN": SCREEN_BASE, + "KEYBOARD": KEYBOARD_ADDR, + "ROM": ROM_BASE, + "HEAP": HEAP_BASE, + }, + **solved_06.register_names(16) +} +def assemble(f): + return solved_06.assemble(f, min_static=None, + start_addr=ROM_BASE, + builtins=BUILTIN_SYMBOLS) + def run(chip, program, name="Flat!", font="monaco-9", halt_addr=None): """Run with keyboard and text-mode graphics.""" # TODO: font + # A little sanity checking: + if len(program) > 32768: + raise Exception(f"Program too large for ROM: {(len(program) - ROM_BASE)/1024:0.1f}K > 30K") + elif any(b != 0 for b in program[:ROM_BASE]): + print("WARNING: non-zero words found in the program image below ROM_BASE; this memory is hidden by low RAM") + computer = nand.syntax.run(chip, simulator="vector") - # print(dir(computer)) computer.init_rom(program) # Jump over low memory that we might be using for debugging: computer.poke(0, solved_06.parse_op(f"@{ROM_BASE}")) computer.poke(1, solved_06.parse_op("0;JMP")) - # print(f"ROM={computer._rom.storage[ROM_BASE:ROM_BASE+10]}") - - # TEMP: splat some chars into the screen ROM - # computer.poke(SCREEN_BASE, 0x41 + 25) - # computer.poke(SCREEN_BASE+40+1, 0x6162) - # computer.poke(SCREEN_BASE+999, 0x40) - - # print(f"{computer._stateful}") kvm = TextKVM(name, 80, 25, 6, 10, "alt/big/Monaco9.png") @@ -274,24 +286,25 @@ def run(chip, program, name="Flat!", font="monaco-9", halt_addr=None): halted = True print(f"halted after {cycles} cycles") - # if not halted: - # print(f"@{computer.pc}; outputs: {computer.outputs()}") - # print(f" R0: {computer.peek(0)}; R1: {computer.peek(1)}") + if not halted: + # print(f"@{computer.pc}; outputs: {computer.outputs()}") + # print(f" R0: {computer.peek(0)}; R1: {computer.peek(1)}") - # computer.ticktock(100) - # cycles += 100 - computer.ticktock() - cycles += 1 + # computer.ticktock(100) + # cycles += 100 + computer.ticktock() + cycles += 1 + else: + time.sleep(0.1) - if cycles % 100 == 0: + if cycles % 100 == 0 or halted: key = kvm.process_events() kvm.update_display(lambda x: computer.peek(SCREEN_BASE + x)) msgs = [ f"{cycles/1000:0.1f}k cycles", f"@{computer.pc}", - f"MEM[SCREEN_BASE]={computer.peek(SCREEN_BASE)}" ] pygame.display.set_caption(f"{name}: {'; '.join(msgs)}") @@ -313,12 +326,10 @@ def main(): print(f"Reading assembly from file: {args.path}") with open(args.path, mode='r') as f: - prg, symbols, statics = solved_06.assemble(f, start_addr=ROM_BASE) - + prg, symbols, statics = assemble(f) print(f"Size in ROM: {len(prg) - ROM_BASE:0,d}") print(f"symbols: {symbols}") - print(f"statics: {statics}") run(chip=BigComputer, program=prg, halt_addr=symbols["halt"]) From 831657debfff4d75b7381846710f2c1719e8a421 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 21 Jan 2024 10:26:17 -0500 Subject: [PATCH 011/221] =?UTF-8?q?Parse=20=E2=80=9C#-=E2=80=9C=20data=20w?= =?UTF-8?q?hen=20assembling?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- alt/big.py | 18 +- alt/big/Output.asm | 720 ++++++++++++++++++++++----------------------- 2 files changed, 377 insertions(+), 361 deletions(-) diff --git a/alt/big.py b/alt/big.py index 72f33a3..7db408e 100755 --- a/alt/big.py +++ b/alt/big.py @@ -251,8 +251,24 @@ def render(self, x, y, c): } +def parse_op(string, symbols): + """Handle 16-bit constants (e.g. #-10 or #0xFF00); these are not necessarily valid instructions. + """ + m = re.match(r"#(-?((0x[0-9a-fA-F]+)|([1-9][0-9]*)|0))", string) + if m: + value = eval(m.group(1)) + if value < -32768 or value > 65535: + raise Exception(f"Constant value out of range: {value} ({string})") + return unsigned(value) + else: + return solved_06.parse_op(string, symbols) + + def assemble(f): - return solved_06.assemble(f, min_static=None, + """Standard assembler, except: no statics, shift the base address, symbols for our base addresses, and 16-bit data.""" + return solved_06.assemble(f, + parse_op=parse_op, + min_static=None, start_addr=ROM_BASE, builtins=BUILTIN_SYMBOLS) diff --git a/alt/big/Output.asm b/alt/big/Output.asm index 38773dd..4d3b325 100644 --- a/alt/big/Output.asm +++ b/alt/big/Output.asm @@ -74,381 +74,381 @@ D;JLE // 40 words per line, for a header and then 8 lines for the lower half of the 8-bit table: (table_start) // header -@0 -@0 -@0 -@0 -@0x3030 -@0 -@0x3130 -@0 -@0x3230 -@0 -@0x3330 -@0 -@0x3430 -@0 -@0x3530 -@0 -@0x3630 -@0 -@0x3730 -@0 -@0x3830 -@0 -@0x3930 -@0 -@0x4130 -@0 -@0x4230 -@0 -@0x4330 -@0 -@0x4430 -@0 -@0x4530 -@0 -@0x4630 -@0 -@0 -@0 -@0 -@0 +#0 +#0 +#0 +#0 +#0x3030 +#0 +#0x3130 +#0 +#0x3230 +#0 +#0x3330 +#0 +#0x3430 +#0 +#0x3530 +#0 +#0x3630 +#0 +#0x3730 +#0 +#0x3830 +#0 +#0x3930 +#0 +#0x4130 +#0 +#0x4230 +#0 +#0x4330 +#0 +#0x4430 +#0 +#0x4530 +#0 +#0x4630 +#0 +#0 +#0 +#0 +#0 // 0x00–0F: -@0 -@0 -@0x3030 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 +#0 +#0 +#0x3030 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 // 0x10–1F: -@0 -@0 -@0x3031 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 -@0 +#0 +#0 +#0x3031 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 // 0x20–2F: -@0 -@0 -@0x3032 -@0 -@0x0020 -@0 -@0x0021 -@0 -@0x0022 -@0 -@0x0023 -@0 -@0x0024 -@0 -@0x0025 -@0 -@0x0026 -@0 -@0x0027 -@0 -@0x0028 -@0 -@0x0029 -@0 -@0x002A -@0 -@0x002B -@0 -@0x002C -@0 -@0x002D -@0 -@0x002E -@0 -@0x002F -@0 -@0 -@0 -@0 -@0 +#0 +#0 +#0x3032 +#0 +#0x0020 +#0 +#0x8021 +#0 +#0x0022 +#0 +#0x0023 +#0 +#0x0024 +#0 +#0x0025 +#0 +#0x0026 +#0 +#0x0027 +#0 +#0x0028 +#0 +#0x0029 +#0 +#0x002A +#0 +#0x002B +#0 +#0x002C +#0 +#0x002D +#0 +#0x002E +#0 +#0x002F +#0 +#0 +#0 +#0 +#0 // 0x30–3F: -@0 -@0 -@0x3033 -@0 -@0x0030 -@0 -@0x0031 -@0 -@0x0032 -@0 -@0x0033 -@0 -@0x0034 -@0 -@0x0035 -@0 -@0x0036 -@0 -@0x0037 -@0 -@0x0038 -@0 -@0x0039 -@0 -@0x003A -@0 -@0x003B -@0 -@0x003C -@0 -@0x003D -@0 -@0x003E -@0 -@0x003F -@0 -@0 -@0 -@0 -@0 +#0 +#0 +#0x3033 +#0 +#0x0030 +#0 +#0x0031 +#0 +#0x0032 +#0 +#0x0033 +#0 +#0x0034 +#0 +#0x0035 +#0 +#0x0036 +#0 +#0x0037 +#0 +#0x0038 +#0 +#0x0039 +#0 +#0x003A +#0 +#0x003B +#0 +#0x003C +#0 +#0x003D +#0 +#0x003E +#0 +#0x003F +#0 +#0 +#0 +#0 +#0 // 0x40–4F: -@0 -@0 -@0x3034 -@0 -@0x0040 -@0 -@0x0041 -@0 -@0x0042 -@0 -@0x0043 -@0 -@0x0044 -@0 -@0x0045 -@0 -@0x0046 -@0 -@0x0047 -@0 -@0x0048 -@0 -@0x0049 -@0 -@0x004A -@0 -@0x004B -@0 -@0x004C -@0 -@0x004D -@0 -@0x004E -@0 -@0x004F -@0 -@0 -@0 -@0 -@0 +#0 +#0 +#0x3034 +#0 +#0x0040 +#0 +#0x0041 +#0 +#0x0042 +#0 +#0x0043 +#0 +#0x0044 +#0 +#0x0045 +#0 +#0x0046 +#0 +#0x0047 +#0 +#0x0048 +#0 +#0x0049 +#0 +#0x004A +#0 +#0x004B +#0 +#0x004C +#0 +#0x004D +#0 +#0x004E +#0 +#0x004F +#0 +#0 +#0 +#0 +#0 // 0x50–5F: -@0 -@0 -@0x3035 -@0 -@0x0050 -@0 -@0x0051 -@0 -@0x0052 -@0 -@0x0053 -@0 -@0x0054 -@0 -@0x0055 -@0 -@0x0056 -@0 -@0x0057 -@0 -@0x0058 -@0 -@0x0059 -@0 -@0x005A -@0 -@0x005B -@0 -@0x005C -@0 -@0x005D -@0 -@0x005E -@0 -@0x005F -@0 -@0 -@0 -@0 -@0 +#0 +#0 +#0x3035 +#0 +#0x0050 +#0 +#0x0051 +#0 +#0x0052 +#0 +#0x0053 +#0 +#0x0054 +#0 +#0x0055 +#0 +#0x0056 +#0 +#0x0057 +#0 +#0x0058 +#0 +#0x0059 +#0 +#0x005A +#0 +#0x005B +#0 +#0x005C +#0 +#0x005D +#0 +#0x005E +#0 +#0x005F +#0 +#0 +#0 +#0 +#0 // 0x60–6F: -@0 -@0 -@0x3036 -@0 -@0x0060 -@0 -@0x0061 -@0 -@0x0062 -@0 -@0x0063 -@0 -@0x0064 -@0 -@0x0065 -@0 -@0x0066 -@0 -@0x0067 -@0 -@0x0068 -@0 -@0x0069 -@0 -@0x006A -@0 -@0x006B -@0 -@0x006C -@0 -@0x006D -@0 -@0x006E -@0 -@0x006F -@0 -@0 -@0 -@0 -@0 +#0 +#0 +#0x3036 +#0 +#0x0060 +#0 +#0x0061 +#0 +#0x0062 +#0 +#0x0063 +#0 +#0x0064 +#0 +#0x0065 +#0 +#0x0066 +#0 +#0x0067 +#0 +#0x0068 +#0 +#0x0069 +#0 +#0x006A +#0 +#0x006B +#0 +#0x006C +#0 +#0x006D +#0 +#0x006E +#0 +#0x006F +#0 +#0 +#0 +#0 +#0 // 0x70–7F: -@0 -@0 -@0x3037 -@0 -@0x0070 -@0 -@0x0071 -@0 -@0x0072 -@0 -@0x0073 -@0 -@0x0074 -@0 -@0x0075 -@0 -@0x0076 -@0 -@0x0077 -@0 -@0x0078 -@0 -@0x0079 -@0 -@0x007A -@0 -@0x007B -@0 -@0x007C -@0 -@0x007D -@0 -@0x007E -@0 -@0x007F -@0 -@0 -@0 -@0 -@0 +#0 +#0 +#0x3037 +#0 +#0x0070 +#0 +#0x0071 +#0 +#0x0072 +#0 +#0x0073 +#0 +#0x0074 +#0 +#0x0075 +#0 +#0x0076 +#0 +#0x0077 +#0 +#0x0078 +#0 +#0x0079 +#0 +#0x007A +#0 +#0x007B +#0 +#0x007C +#0 +#0x007D +#0 +#0x007E +#0 +#0x007F +#0 +#0 +#0 +#0 +#0 (table_end) From 98098b299be23700497284f21ef1587b8d278d23 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 21 Jan 2024 10:26:43 -0500 Subject: [PATCH 012/221] Comment, organize, and cleanup --- alt/big.py | 137 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 81 insertions(+), 56 deletions(-) diff --git a/alt/big.py b/alt/big.py index 7db408e..e5ba449 100755 --- a/alt/big.py +++ b/alt/big.py @@ -2,9 +2,13 @@ """A computer with a single 16-bit address space for ROM and RAM. -Uses the same ISA and assembler as the normal Hack CPU. +Uses the same ISA and assembler as the normal Hack CPU, with a few extensions. -Both instructions and data can be read from any address. +Both instructions and data can be read from any address. *Note: because instructions and +data share the single memory bus, it now takes 2 cycles to fetch and execute most if not +all instructions.* This is more or less authentic to early integrated CPUs, which were +designed to minimize pinout while making effective use of whatever memory the customer +was able to afford. Writes to ROM addresses are ignored. @@ -21,7 +25,6 @@ - The ROM lives in the 15-bit addressable range, so a code address can always be loaded in one cycle - "negative" addresses are a uniform block of RAM, useful for heap - | Address Range | Size (words) | Storage | Contents | | 0x0000–0x03FF | 1K | RAM | Temporaries, "registers", etc. | | 0x0400–0x07FF | 1K | RAM | Screen buffer and I/O | @@ -33,28 +36,34 @@ at 0x0800 = 2048. TODO: make the size of the ROM configurable, so you can trade off heap space vs runtime size. +That means synthesizing the logic to overlay the right adress range, so maybe you just select +one of a few available sizes, e.g. 2K, 6K, 10K, 14K, 30K? + +"Character-mode" graphics make more efficient use of memory, with only 1k allocated to the screen +as opposed to 8K for a similar number of pixels (assuming 9-point font.) It also means at least +8x faster updates, For authentic Macintosh fonts, see https://archive.org/details/AppleMacintoshSystem753 """ from nand import chip, lazy, RAM, ROM, Input, Output, DFF import nand.syntax +from nand.vector import unsigned from project_01 import And, Or, Not from project_03 import Mux, Mux16, Register, PC, ALU -import project_05 from nand.solutions import solved_06 from alt.threaded import Eq16 -import computer -import pygame.image -import time - - SCREEN_BASE = 0x0400 KEYBOARD_ADDR = 0x07FF ROM_BASE = 0x0800 HEAP_BASE = 0x8000 + +# +# Components: +# + @chip def FlatMemory(inputs, outputs): """The same interface as MemorySystem, but also maps the ROM and extends address to the full 16 bits. @@ -188,56 +197,21 @@ def BigComputer(inputs, outputs): # needs to be forced to be included. outputs.tty_ready = mem.tty_ready - # TEMP: - outputs.fetch = fetch - # outputs.execute = execute - outputs.addr = addr - # outputs.addressM = cpu.addressM - outputs.writeM = cpu.writeM - outputs.outM = cpu.outM - outputs.instr = instr_reg.out - - -class TextKVM(computer.KVM): - """Keyboard and display, displaying characters using a set of baked-in glyphs. - - Each word of the screen buffer stores a pair of 8-bit characters. - """ - - def __init__(self, title, char_width, char_height, glyph_width, glyph_height, bitmap_path): - computer.KVM.__init__(self, title, char_width*glyph_width, char_height*glyph_height) - - self.char_width = char_width - self.char_height = char_height - self.glyph_width = glyph_width - self.glyph_height = glyph_height - - self.glyph_sheet = pygame.image.load(bitmap_path) - - - def update_display(self, get_chars): - self.screen.fill(computer.COLORS[0]) - - stride = self.char_width//2 - for y in range(0, self.char_height): - for x in range(0, self.char_width, 2): - pair = get_chars(y*stride + x//2) - self.render( x, y, pair & 0xFF) - self.render(x+1, y, pair >> 8) + # # TEMP: + # outputs.fetch = fetch + # # outputs.execute = execute + # outputs.addr = addr + # # outputs.addressM = cpu.addressM + # outputs.writeM = cpu.writeM + # outputs.outM = cpu.outM + # outputs.instr = instr_reg.out - pygame.display.flip() +# +# Assembler: +# - def render(self, x, y, c): - g_x = (c & 0x0F)*self.glyph_width - g_y = (c >> 4)*self.glyph_height - self.screen.blit(self.glyph_sheet, - dest=(x*self.glyph_width, - y*self.glyph_height), - area=pygame.Rect(g_x, - g_y, - self.glyph_width, - self.glyph_height)) +import re BUILTIN_SYMBOLS = { @@ -273,6 +247,15 @@ def assemble(f): builtins=BUILTIN_SYMBOLS) +# +# Harness: +# + +import computer +import pygame.image +import time + + def run(chip, program, name="Flat!", font="monaco-9", halt_addr=None): """Run with keyboard and text-mode graphics.""" @@ -325,6 +308,48 @@ def run(chip, program, name="Flat!", font="monaco-9", halt_addr=None): pygame.display.set_caption(f"{name}: {'; '.join(msgs)}") +class TextKVM(computer.KVM): + """Keyboard and display, displaying characters using a set of baked-in glyphs. + + Each word of the screen buffer stores a pair of 8-bit characters. + """ + + def __init__(self, title, char_width, char_height, glyph_width, glyph_height, bitmap_path): + computer.KVM.__init__(self, title, char_width*glyph_width, char_height*glyph_height) + + self.char_width = char_width + self.char_height = char_height + self.glyph_width = glyph_width + self.glyph_height = glyph_height + + self.glyph_sheet = pygame.image.load(bitmap_path) + + + def update_display(self, get_chars): + self.screen.fill(computer.COLORS[0]) + + stride = self.char_width//2 + for y in range(0, self.char_height): + for x in range(0, self.char_width, 2): + pair = get_chars(y*stride + x//2) + self.render( x, y, pair & 0xFF) + self.render(x+1, y, pair >> 8) + + pygame.display.flip() + + + def render(self, x, y, c): + g_x = (c & 0x0F)*self.glyph_width + g_y = (c >> 4)*self.glyph_height + self.screen.blit(self.glyph_sheet, + dest=(x*self.glyph_width, + y*self.glyph_height), + area=pygame.Rect(g_x, + g_y, + self.glyph_width, + self.glyph_height)) + + def main(): import argparse parser = argparse.ArgumentParser(description="Run assembly source with text-mode display and keyboard") From efdef913bec2d961fa8cc2f36876e665c563aa28 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 21 Jan 2024 14:25:04 -0500 Subject: [PATCH 013/221] More ROM size feedback --- alt/big.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/alt/big.py b/alt/big.py index e5ba449..0fc2606 100755 --- a/alt/big.py +++ b/alt/big.py @@ -225,7 +225,7 @@ def BigComputer(inputs, outputs): } -def parse_op(string, symbols): +def parse_op(string, symbols={}): """Handle 16-bit constants (e.g. #-10 or #0xFF00); these are not necessarily valid instructions. """ m = re.match(r"#(-?((0x[0-9a-fA-F]+)|([1-9][0-9]*)|0))", string) @@ -262,8 +262,8 @@ def run(chip, program, name="Flat!", font="monaco-9", halt_addr=None): # TODO: font # A little sanity checking: - if len(program) > 32768: - raise Exception(f"Program too large for ROM: {(len(program) - ROM_BASE)/1024:0.1f}K > 30K") + if len(program) > HEAP_BASE: + raise Exception(f"Program too large for ROM: {(len(program) - ROM_BASE)/1024:0.1f}K > {(HEAP_BASE - ROM_BASE)//1024}K") elif any(b != 0 for b in program[:ROM_BASE]): print("WARNING: non-zero words found in the program image below ROM_BASE; this memory is hidden by low RAM") @@ -272,8 +272,8 @@ def run(chip, program, name="Flat!", font="monaco-9", halt_addr=None): computer.init_rom(program) # Jump over low memory that we might be using for debugging: - computer.poke(0, solved_06.parse_op(f"@{ROM_BASE}")) - computer.poke(1, solved_06.parse_op("0;JMP")) + computer.poke(0, ROM_BASE) + computer.poke(1, parse_op("0;JMP")) kvm = TextKVM(name, 80, 25, 6, 10, "alt/big/Monaco9.png") @@ -369,7 +369,9 @@ def main(): with open(args.path, mode='r') as f: prg, symbols, statics = assemble(f) - print(f"Size in ROM: {len(prg) - ROM_BASE:0,d}") + prg_size = len(prg) - ROM_BASE + max_size = HEAP_BASE - ROM_BASE + print(f"Size in ROM: {prg_size:0,d} ({100*prg_size/max_size:0.1f}% of {max_size//1024}K)") print(f"symbols: {symbols}") run(chip=BigComputer, program=prg, halt_addr=symbols["halt"]) From 15118a5a9a17786151949725d00b18cd9fc46757 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 21 Jan 2024 14:53:01 -0500 Subject: [PATCH 014/221] Handle keydown and TTY --- alt/big.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/alt/big.py b/alt/big.py index 0fc2606..d80aeb2 100755 --- a/alt/big.py +++ b/alt/big.py @@ -299,6 +299,12 @@ def run(chip, program, name="Flat!", font="monaco-9", halt_addr=None): if cycles % 100 == 0 or halted: key = kvm.process_events() + computer.set_keydown(key or 0) + + tty_char = computer.get_tty() + if tty_char: + print(f"tty: {tty_char} ('{chr(tty_char)}')") + kvm.update_display(lambda x: computer.peek(SCREEN_BASE + x)) msgs = [ From 53101729bba6beee410961d6f543718f6139f8cb Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 21 Jan 2024 15:14:30 -0500 Subject: [PATCH 015/221] Fix spurious writes when reading from keyboard --- alt/big.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alt/big.py b/alt/big.py index d80aeb2..8b4a898 100755 --- a/alt/big.py +++ b/alt/big.py @@ -92,7 +92,7 @@ def FlatMemory(inputs, outputs): rom = ROM(15)(address=address) # Based at 0 and sized to 32K, but partially hidden by RAM # screen = RAM(10) # Separate RAM would make life easier for the harness? keyboard = Input() - tty = Output(in_=in_, load=is_io) + tty = Output(in_=in_, load=And(a=load, b=is_io).out) outputs.out = Mux16(sel=is_io, a=Mux16(sel=is_rom, a=ram.out, b=rom.out).out, From d1df3dbcadadead5d98ff0026ca06ebdccac564f Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 21 Jan 2024 15:16:01 -0500 Subject: [PATCH 016/221] In-progress: practice moving chars between keyboard and screen --- alt/big/Echo.asm | 99 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 alt/big/Echo.asm diff --git a/alt/big/Echo.asm b/alt/big/Echo.asm new file mode 100644 index 0000000..cc69a0e --- /dev/null +++ b/alt/big/Echo.asm @@ -0,0 +1,99 @@ +// Read characters from the keyboard and echo them to the screen, starting at the top line. +// - backspace (129) will delete characters to the left, until the beginning of the current line +// - newline (128) moves the insertion point to the beginning of the next line +// - typing off the end of the line wraps to the next line +// - when continuing past the bottom line, the previous 24 lines are "scrolled" up, and the +// top line is discarded +// - esc (140) stops accepting input, tells the simulator the program is complete +// +// Future: make it a free-form editor, showing a window of 25 lines from a 32K buffer? + +// +// Initialize some state: +// + +// R0: previous key code +@R0 +M=0 +// R1: insertion point column/2 +@R1 +M=0 +// R2: insertion point column odd/even flag (i.e. the low bit) +@R2 +M=0 +// R3: current line +@R3 +M=0 + + +// +// Infinite loop: +// + +(loop) +// Read current key down: +@KEYBOARD +D=M + +// Compare with previous: +@R5 +M=D +@R0 +D=M-D +@loop +D;JEQ +@R5 +D=M +// Store current key: +@R0 +M=D + +// Compare with zero (no key down): +@loop +D;JEQ + +// Compare with ESC: +@140 +D=A +@R0 +D=M-D +@halt +D;JEQ + +// TODO: backspace (@129) +// TODO: newline (@128) + +// R5 = address of insertion point: +// TODO: half-word +@R1 +D=M +@SCREEN +D=D+A +@R5 +M=D +// Write current key to the screen +@R0 +D=M +@R5 +A=M +M=D +// Also echo to the TTY for debug purposes +@KEYBOARD +M=D + +// Move to the right: +// TODO: half-word at a time +@R1 +M=M+1 + +@loop +0;JMP + + +// +// Halt loop: required by the harness, but also a place to go on ESC +// + +(halt) +@halt +0;JMP From 27e3f9c3680fbe392d6d3381e22a2e5d50ccb8ba Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 21 Jan 2024 15:49:36 -0500 Subject: [PATCH 017/221] Correct some alignment errors --- alt/big/Monaco9.png | Bin 862 -> 865 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/alt/big/Monaco9.png b/alt/big/Monaco9.png index c7663304ebe0717bcf827760a041973f46c990ac..823e741adf536d8ecb95413c8cbf678fd0da5d54 100644 GIT binary patch delta 675 zcmV;U0$lyx2H^&f$$x`ML_t(&f$dj|a)U4oi~s+pcPnu$zXG&tx7$hEK?;dw8_R() z2!bF8g81W*H<**hX&z-B<@*Sf`FP1mBanwt|EITMjSRiFp@{@K6bNRG<2=zQdjcuP zqG^G2mGksZCXcP%6R5*8UzVmE!O4@@KB4LvxK)j#EkT-e8nx8^8n(}d@#4aJb zc+dLB_XRtTXeB&oMPj%u+dFwM*S_cWc+Oi)LFrR%mS?Fa&? z2>xQarh~B=#DcZ0RwD?4+C@;ONXj`Yg8If40`qr6fVI(9(R4OdQCJ&&zp!Tzqf+k( z+20)D-FsCP6z$MqwK&v?7mhDXZxYL|Cm20Js3T-@XMdlfT&8;h^&^P5BuH_1<46!^ zc+>v0At+L@+KV{fOUpHZQ)%9TQ#7jyz!#h@SeE0f&C9y}zl}kPQTlD4dG0Lf%9f!g z=$*JiaCj&|X!&aZ`<0KtDs5I{`i7_*M&FmRerz1xDry339;XD72u4L9C4qCGivYFV z5X>+7kbiwkxvIJ$a1Ok*CvOV*2avy-KGz(4;%+RuGZp0_S5`kyAecO?98;jUn!);T zSDdUNf=F%)cC|mv`?reFHVTHU;eOL^-YjlJ5U#4i&Girg2HO(2X8>2GX^^}#4c~6( z5ya6oNEkOdc#?qc{sch~1VIo4K@bE%5ClOG1d~7m1sKFj_yV+P1X=&Kc`g6|002ov JPDHLkV1j1(JYoO< delta 673 zcmV;S0$%;$2Hpmc$$x-JL_t(&f$dlUa>FnPGynheZmlsAU^iXc-EJpsbKS&7Km?}iOyCaBd_|gS1P`u+ZNH#@a1ChT#?h0_bbt1q{i&$JP&b!+*!J`l z{>GzM4LocA*MM4H)$2Z?WLduU*kc#^w6x}HX7z$vTEhl;=9Xg$tGs;8f~F(LeaQ=r z`Uw%tOs!LQbC5J@g1!HVv+>1^`mwyOuY?)_W(rCJztw~s8bqn~xh_;hI!Or${&M8= zCGfLe^t%1u0)OPx%6#5Fv7Se+n-D@0g_Y{<$I)$Of~a|$zf7|;S_%3HP`Y%R@{VAT zisUb*YdV;lK^AP>YCVEfP^Sp`6j{4wMbMwPLJJp1qfEde>OivVkT zLonayQ-Ag&^ z!P4}-1kD2jH#b2L1VIo4K@bE%5ClOG1VIo4K^hPQK{(+H3fBZ%Am*&E00000NkvXX Hu0mjfd!s=D From 2bc6ae4a3bef18b9d3b48aa71cf5886e683b6f2e Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 21 Jan 2024 16:03:37 -0500 Subject: [PATCH 018/221] Fix alignment for delimiters and 1 According to System 7.5.3 in emulation. --- alt/big/Monaco9.png | Bin 865 -> 858 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/alt/big/Monaco9.png b/alt/big/Monaco9.png index 823e741adf536d8ecb95413c8cbf678fd0da5d54..5f8ae218a4b8b394caf199a4d040de8fc35b0989 100644 GIT binary patch delta 669 zcmV;O0%HB)2HFOY$$xxFL_t(&f$diba^o-z6aW9|&Nh+=@KEezdrSvt$B|`;AZd`c z#}EWT5Cq|u!@j_reg4d&&7=K0BBk;0qLWTwpGNs_UFM^0kFFA|p+PWv92R<%J%N>D zTWNuG6$@=eFxTVWGr@9r=F8KRBcP<611K)$dw|;-=X!#)=zo!?mFvh**i4i+Bz6he z#hV81+W$5flyO&4{hX*Sf;vT3uGtaP7p@SvzXt-;MysOfY*JC!8-2g<%n(LZy(47*aNHY) z->1h95Hy>EPPlNAFj2O9@rhvc1VKl9Am1@XS*B-#<$nhxq$Nmk#K4^(u3)1n`yD}% zirrp}e79tyC7>$JPv8{IZUS3Hd&(`xSDUx%`tLRdD@N(uEX=Xms@;X1o}hQ)3cfl8Jz55G-AP9mW2!bF8f*=TjAP9mW2!bF8Zx-PLv|$8U^w0hX00000NkvXXu0mjf D30*|` delta 676 zcmV;V0$css2H^&f$$x`ML_t(&f$dj|a)U4oi~s+pcPnu$zXG&tx7$hEK?;dw8_R() z2!bF8g81W*H<**hX&z-B<@*Sf`FP1mBanwt|EITMjSRiFp@{@K6bNRG<2=zQdjcuP zqG^G2mGksZCXcP%6R5*8UzVmE!O4@@KB4LvxK)j#EkT-e8nx8^8n(}d@#4aJb zc+dLB_XRtTXeB&oMPj%u+dFwM*S_cWc+Oi)LFrR%mS?Fa&? z2>xQarh~B=#DcZ0RwD?4+C@;ONXj`Yg8If40`qr6fVI(9(R4OdQCJ&&zp!Tzqf+k( z+20)D-FsCP6z$MqwK&v?7mhDXZxYL|Cm20Js3T-@XMdlfT&8;h^&^P5BuH_1<46!^ zc+>v0At+L@+KV{fOUpHZQ)%9TQ#7jyz!#h@SeE0f&C9y}zl}kPQTlD4dG0Lf%9f!g z=$*JiaCj&|X!&aZ`<0KtDs5I{`i7_*M&FmRerz1xDry339;XD72u4L9C4qCGivYFV z5X>+7kbiwkxvIJ$a1Ok*CvOV*2avy-KGz(4;%+RuGZp0_S5`kyAecO?98;jUn!);T zSDdUNf=F%)cC|mv`?reFHVTHU;eOL^-YjlJ5U#4i&Girg2HO(2X8>2GX^^}#4c~6( z5ya6oNEkOdc#?qc{sch~1VIo4K@bE%5ClOG1RX&T1VIqQOZWn`X#`pSws|f90000< KMNUMnLSTZ4PCPCE From 3cd2e323e4b8c5d1e8d87e0bc2e8d6b98789e2f8 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 21 Jan 2024 16:04:04 -0500 Subject: [PATCH 019/221] Commit the screenshot for reference --- alt/big/System7.5.3.png | Bin 0 -> 16229 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 alt/big/System7.5.3.png diff --git a/alt/big/System7.5.3.png b/alt/big/System7.5.3.png new file mode 100644 index 0000000000000000000000000000000000000000..c8de461642f48b268c883061da41f0dce732523a GIT binary patch literal 16229 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 literal 0 HcmV?d00001 From a8e8f5ef53ce3237fb18abad67afb6daf3789389 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 21 Jan 2024 20:00:26 -0500 Subject: [PATCH 020/221] Actually, assembler-allocated statics are a great idea after all --- alt/big.py | 15 +++++++++------ alt/big/Echo.asm | 38 +++++++++++++++++++------------------- alt/big/Output.asm | 18 +++++++++--------- 3 files changed, 37 insertions(+), 34 deletions(-) diff --git a/alt/big.py b/alt/big.py index 8b4a898..855df47 100755 --- a/alt/big.py +++ b/alt/big.py @@ -68,8 +68,9 @@ def FlatMemory(inputs, outputs): """The same interface as MemorySystem, but also maps the ROM and extends address to the full 16 bits. - Note: this will need some additional support in the "codegen" simulator, which otherwise implements - the standard MemoryStstem directly. + FIXME: this will need some additional support in the "codegen" simulator, which otherwise implements + the standard MemorySystem directly. But that support will be useful to any Computer with different + memory layout. """ in_ = inputs.in_ @@ -226,7 +227,7 @@ def BigComputer(inputs, outputs): def parse_op(string, symbols={}): - """Handle 16-bit constants (e.g. #-10 or #0xFF00); these are not necessarily valid instructions. + """Handle 16-bit constants (e.g. #-10 or #0xFF00); used for data to be read from ROM. """ m = re.match(r"#(-?((0x[0-9a-fA-F]+)|([1-9][0-9]*)|0))", string) if m: @@ -238,11 +239,12 @@ def parse_op(string, symbols={}): return solved_06.parse_op(string, symbols) -def assemble(f): - """Standard assembler, except: no statics, shift the base address, symbols for our base addresses, and 16-bit data.""" +def assemble(f, min_static=16, max_static=1023): + """Standard assembler, except: shift the ROM base address, symbols for other base addresses, and 16-bit data.""" return solved_06.assemble(f, parse_op=parse_op, - min_static=None, + min_static=min_static, + max_static=max_static, start_addr=ROM_BASE, builtins=BUILTIN_SYMBOLS) @@ -379,6 +381,7 @@ def main(): max_size = HEAP_BASE - ROM_BASE print(f"Size in ROM: {prg_size:0,d} ({100*prg_size/max_size:0.1f}% of {max_size//1024}K)") print(f"symbols: {symbols}") + print(f"statics: {statics}") run(chip=BigComputer, program=prg, halt_addr=symbols["halt"]) diff --git a/alt/big/Echo.asm b/alt/big/Echo.asm index cc69a0e..37581bf 100644 --- a/alt/big/Echo.asm +++ b/alt/big/Echo.asm @@ -12,17 +12,17 @@ // Initialize some state: // -// R0: previous key code -@R0 +// PREVIOUS_KEY: key code that was down on previous iteration +@PREVIOUS_KEY M=0 -// R1: insertion point column/2 -@R1 +// INSERTION_COLUMN: insertion point column/2 +@INSERTION_COLUMN M=0 -// R2: insertion point column odd/even flag (i.e. the low bit) -@R2 +// INSERTION_HIGH: insertion point column odd/even flag (i.e. the low bit) +@INSERTION_HIGH M=0 -// R3: current line -@R3 +// CURRENT_LINE: current line +@CURRENT_LINE M=0 @@ -36,16 +36,16 @@ M=0 D=M // Compare with previous: -@R5 -M=D @R0 +M=D +@PREVIOUS_KEY D=M-D @loop D;JEQ -@R5 +@R0 D=M // Store current key: -@R0 +@PREVIOUS_KEY M=D // Compare with zero (no key down): @@ -55,7 +55,7 @@ D;JEQ // Compare with ESC: @140 D=A -@R0 +@PREVIOUS_KEY D=M-D @halt D;JEQ @@ -63,18 +63,18 @@ D;JEQ // TODO: backspace (@129) // TODO: newline (@128) -// R5 = address of insertion point: +// R0 = address of insertion point: // TODO: half-word -@R1 +@INSERTION_COLUMN D=M @SCREEN D=D+A -@R5 +@R0 M=D // Write current key to the screen -@R0 +@PREVIOUS_KEY D=M -@R5 +@R0 A=M M=D // Also echo to the TTY for debug purposes @@ -83,7 +83,7 @@ M=D // Move to the right: // TODO: half-word at a time -@R1 +@INSERTION_COLUMN M=M+1 @loop diff --git a/alt/big/Output.asm b/alt/big/Output.asm index 4d3b325..60f305d 100644 --- a/alt/big/Output.asm +++ b/alt/big/Output.asm @@ -35,31 +35,31 @@ D=A M=D (copy_start) -// R0 = start of third row of screen buffer +// DST = start of third row of screen buffer @1104 D=A -@R0 +@DST M=D -// R1 = @table_start (start of the table in ROM) +// SRC = @table_start (start of the table in ROM) @table_start D=A -@R1 +@SRC M=D (copy_loop) -// D = MEM[R1++] -@R1 +// D = MEM[SRC++] +@SRC M=M+1 A=M-1 D=M -// MEM[R0++] = D -@R0 +// MEM[DST++] = D +@DST M=M+1 A=M-1 M=D // Check for end of table: -@R1 +@SRC D=M @table_end D=D-A From b504b0f7264e5b5eda30049ab41a2bb282fb5a19 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 21 Jan 2024 20:02:08 -0500 Subject: [PATCH 021/221] Deal with shifted keys being held down --- computer.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/computer.py b/computer.py index 925c579..0e937da 100755 --- a/computer.py +++ b/computer.py @@ -154,6 +154,16 @@ def load(platform, path, print_asm=False, no_waiting=False): ] + [ (c, c) for c in range(32, 127) ]) # Printable characters, plus a few odd-balls +SHIFTED_KEY_MAP = { + **KEY_MAP, + **dict((ord(x), ord(y)) for x, y in + zip("abcdefghijklmnopqrstuvwxyz`1234567890-=[]\\;',./", + "ABCDEFGHIJKLMNOPQRSTUVWXYZ~!@#$%^&*()_+{}|:\"<>?")) +} +"""Map from raw key code to the code produced when (any) shift modifier is down. +Note: this is definitely not correct if your keyboard layout isn't a typical US layout. +Not sure +""" class KVM: def __init__(self, title, width, height): @@ -189,7 +199,9 @@ def process_events(self): return typed_keys[0] keys = pygame.key.get_pressed() - for idx, key in KEY_MAP.items(): + mods = pygame.key.get_mods() + shifted = mods & pygame.KMOD_SHIFT or mods & pygame.KMOD_LSHIFT or mods & pygame.KMOD_RSHIFT + for idx, key in (KEY_MAP if not shifted else SHIFTED_KEY_MAP).items(): if keys[idx]: return key From 27f283be802c1bba9e148dc76c555a07e40c97d5 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 21 Jan 2024 20:04:46 -0500 Subject: [PATCH 022/221] Update very outdated comment --- computer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/computer.py b/computer.py index 0e937da..5c41927 100755 --- a/computer.py +++ b/computer.py @@ -4,7 +4,7 @@ The program to run must be in the form of Hack assembly (.asm) or VM opcodes (a directory of .vm files) and is specified by sys.argv[1]. -The `codegen` simulator is used unless --vector is used. +The `codegen` simulator is used unless --simulator is specified. $ ./computer.py examples/Blink.asm From 90435abdaa144120508d153871a533c46a306372 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Mon, 22 Jan 2024 15:04:49 -0500 Subject: [PATCH 023/221] =?UTF-8?q?Update=20to=20clarify=20why=20big.py=20?= =?UTF-8?q?doesn=E2=80=99t=20get=20a=20proper=20comparison?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- alt/README.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/alt/README.md b/alt/README.md index 3da6f15..e40488f 100644 --- a/alt/README.md +++ b/alt/README.md @@ -12,7 +12,7 @@ Four alternative implementations use more or less chip hardware to make programs fit larger programs in ROM: [alt/sp.py](sp.py) adds instructions for pushing/popping values to/from the stack, making programs -more compact. +more compact and efficient. [alt/threaded.py](threaded.py) adds lightweight CALL/RTN instructions, enabling a very compact "threaded interpreter" translation, which runs a little slower. @@ -24,7 +24,11 @@ more compact. [alt/big.py](big.py) has a single, flat memory space, with maximum RAM and the ability to read data from ROM (and code from RAM.) This is much more flexible and realistic, but adds a cycle to -fetch each instruction from the shared memory system. +fetch each instruction from the shared memory system. Moving static data to ROM can dramtically +improve code size and performance, but because the computer uses character-mode graphics, these +metrics don't provide a direct comparison (even if you did port the VM and OS, which I haven't.) +This architecture is intended to support more sophisticated languages (e.g. BASIC, Scheme, or Forth), +and interactive programming. ## Enhanced compiler/translators @@ -48,7 +52,7 @@ replaces certain function calls with lower-overhead "reduced" alternatives. | [alt/threaded.py](threaded.py) | 1,549 (+23%) | 8,100 (-68%) | 49,600 (+20%) | 173,750 (+34%) | | [alt/shift.py](shift.py) | 1,311 (+4%) | 26,050 (+1%) | 19,800 (-52%) | _same_ | | [alt/eight.py](eight.py) | 1,032 (-18%) | _same_ | +100% | +100% | -| [alt/big.py](big.py) | 1,444 (+14%) | *TBD* | +100% | +100% | +| [alt/big.py](big.py) | 1,448 (+14%) | ? | ? | ? | | [alt/lazy.py](lazy.py) | _same_ | 23,650 (-8%) | 37,300 (-10%) | 111,000 (-14%) | | [alt/reg.py](reg.py) | _same_ | 20,900 (-19%) | 19,150 (-54%) | 59,000 (-54%) | | [alt/reduce.py](reduce.py) | _same_ | 27,350 (+6.5%) | 20,300 (-51%) | _same_ | From 00723aea2cc673df83866b0f60729a5c04849687 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Mon, 22 Jan 2024 15:05:42 -0500 Subject: [PATCH 024/221] Write raw characters from tty to stdout --- alt/big.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alt/big.py b/alt/big.py index 855df47..cdc4822 100755 --- a/alt/big.py +++ b/alt/big.py @@ -305,7 +305,7 @@ def run(chip, program, name="Flat!", font="monaco-9", halt_addr=None): tty_char = computer.get_tty() if tty_char: - print(f"tty: {tty_char} ('{chr(tty_char)}')") + print(chr(tty_char), end="", flush=True) kvm.update_display(lambda x: computer.peek(SCREEN_BASE + x)) From a9fac8c2c2a730e935749d9805d80486fc9ec62c Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Mon, 22 Jan 2024 15:06:11 -0500 Subject: [PATCH 025/221] Fix ROM size TODO to make more sense --- alt/big.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/alt/big.py b/alt/big.py index cdc4822..1244110 100755 --- a/alt/big.py +++ b/alt/big.py @@ -36,8 +36,8 @@ at 0x0800 = 2048. TODO: make the size of the ROM configurable, so you can trade off heap space vs runtime size. -That means synthesizing the logic to overlay the right adress range, so maybe you just select -one of a few available sizes, e.g. 2K, 6K, 10K, 14K, 30K? +That means synthesizing the logic to overlay the right address range, so maybe you just select +one of a few available sizes, e.g. 2K, 6K, 14K, 30K? "Character-mode" graphics make more efficient use of memory, with only 1k allocated to the screen as opposed to 8K for a similar number of pixels (assuming 9-point font.) It also means at least From 944cc3f0f758919a98575c7eb044ffb996d53b3c Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Mon, 22 Jan 2024 15:06:23 -0500 Subject: [PATCH 026/221] Update for latest --- alt/test_big.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/alt/test_big.py b/alt/test_big.py index 063305f..44434c1 100755 --- a/alt/test_big.py +++ b/alt/test_big.py @@ -7,7 +7,7 @@ def test_memory_system(): # FIXME: not handled by "codegen" yet - mem = run(FlatMemory, simulator="vector") + mem = nand.run(FlatMemory, simulator="vector") # set RAM[0] = -1 mem.in_ = -1 @@ -114,7 +114,7 @@ def test_memory_system(): def test_rom(): - mem = run(FlatMemory, simulator="vector") + mem = nand.run(FlatMemory, simulator="vector") # Writes to any ROM-mapped address are ignored: for addr in (ROM_BASE, ROM_BASE + 1234, ROM_BASE + 29000, HEAP_BASE - 1): @@ -147,7 +147,7 @@ def test_gates_mem(): This would have been extra chips on the board, not extra gates in the CPU, presumably. """ - assert gate_count(FlatMemory)['nands'] == 220 + assert gate_count(FlatMemory)['nands'] == 222 import project_05 assert gate_count(project_05.MemorySystem)['nands'] == 163 @@ -350,14 +350,14 @@ def test_gates_mem(): def test_gates_cpu(): """Portion of the extra chip size that's in the CPU's "idle" state.""" - assert gate_count(IdlableCPU)['nands'] == 1104 + assert gate_count(IdlableCPU)['nands'] == 1106 import project_05 assert gate_count(project_05.CPU)['nands'] == 1099 def test_computer_no_program(): - computer = run(BigComputer) + computer = nand.run(BigComputer) for _ in range(100): computer.ticktock() @@ -595,7 +595,7 @@ def test_computer_no_program(): def test_gates_computer(): """Overall extra chip size.""" - assert gate_count(BigComputer)['nands'] == 1444 + assert gate_count(BigComputer)['nands'] == 1448 import project_05 assert gate_count(project_05.Computer)['nands'] == 1262 From 8f62cd3a96774b0e4a9adda588dc0592387eba4f Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Mon, 22 Jan 2024 15:44:37 -0500 Subject: [PATCH 027/221] A different hack for accessing SP --- nand/vector.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/nand/vector.py b/nand/vector.py index dea75cd..9816f63 100644 --- a/nand/vector.py +++ b/nand/vector.py @@ -716,8 +716,13 @@ def get_tty(self): # that subclasses can override. @property def sp(self): - """Read the current value of the stack pointer, which is normally stored at RAM[0].""" - return self.peek(0) + """Read the current value of the stack pointer, which is normally stored at RAM[0], but may + be an ordinary output in some cases. + """ + if "sp" in self.outputs(): + return self.__getattr__("sp") + else: + return self.peek(0) def __repr__(self): return str(self.outputs()) From 2dc5a1325b10fb3f665eb821d5a3f9cb53a57629 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Mon, 22 Jan 2024 15:48:35 -0500 Subject: [PATCH 028/221] Improve some random comments --- alt/sp.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/alt/sp.py b/alt/sp.py index 07fcbd8..11da4fd 100755 --- a/alt/sp.py +++ b/alt/sp.py @@ -252,11 +252,17 @@ def _pop_segment(self, segment_name, segment_ptr, index): self.asm.instr("M=D") def _push_d(self): - # TODO: no need for this as soon as everything's switched to use SP++ directly + """Push the value in D. When the value came from memory, it still needs to be stored in D + temporarily so the push op can access the bus. So the superclass's implementation will + use this one-cycle push sequence, and there's no need to re-implement those operarations + here.""" self.asm.instr("SP++=D") def _pop_d(self): - # TODO: no need for this as soon as everything's switched to use --SP directly? + """Pop the top of the statck to D. When the value will be written to memory, it still + needs to be stored in D temporarily so the pop op can access the bus. So the superclass's + implementation will use this one-cycle pop sequence, and there's no need to re-implement + those operarations here.""" self.asm.instr("D=--SP") def _binary(self, opcode, op): @@ -386,6 +392,8 @@ def _call(self): # TODO: improve the common sequence for `return`. + # That would save one cycle per word of saved state per call, but minimal ROM space, since + # the sequence is shared. def finish(self): From 2441a83f73bd63dec7d4cae81ca29c0d9350ecd9 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sat, 27 Jan 2024 11:07:21 -0500 Subject: [PATCH 029/221] First steps towards executing (Ribbit) scheme Run rsc and decode the symbol table into ROM and (at runtime) RAM. Not really dealing with instructions yet, but the path is becoming clear. --- alt/big.py | 23 +- alt/scheme/repl.py | 30 +++ alt/scheme/ribbit/rsc.py | 87 +++++++ alt/scheme/rvm.py | 537 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 671 insertions(+), 6 deletions(-) create mode 100755 alt/scheme/repl.py create mode 100644 alt/scheme/ribbit/rsc.py create mode 100755 alt/scheme/rvm.py diff --git a/alt/big.py b/alt/big.py index 1244110..48e1eb7 100755 --- a/alt/big.py +++ b/alt/big.py @@ -58,6 +58,7 @@ KEYBOARD_ADDR = 0x07FF ROM_BASE = 0x0800 HEAP_BASE = 0x8000 +HEAP_TOP = 0xFFFF # @@ -198,8 +199,8 @@ def BigComputer(inputs, outputs): # needs to be forced to be included. outputs.tty_ready = mem.tty_ready - # # TEMP: - # outputs.fetch = fetch + outputs.fetch = fetch # exposed so debuggers can track half-cycles + # DEBUG: # # outputs.execute = execute # outputs.addr = addr # # outputs.addressM = cpu.addressM @@ -258,7 +259,7 @@ def assemble(f, min_static=16, max_static=1023): import time -def run(chip, program, name="Flat!", font="monaco-9", halt_addr=None): +def run(program, chip=BigComputer, name="Flat!", font="monaco-9", halt_addr=None, trace=None): """Run with keyboard and text-mode graphics.""" # TODO: font @@ -299,13 +300,23 @@ def run(chip, program, name="Flat!", font="monaco-9", halt_addr=None): time.sleep(0.1) - if cycles % 100 == 0 or halted: + if trace is not None: + trace(computer, cycles) + CYCLES_PER_UPDATE = 5 + else: + CYCLES_PER_UPDATE = 100 + + if cycles % CYCLES_PER_UPDATE == 0 or halted: key = kvm.process_events() computer.set_keydown(key or 0) tty_char = computer.get_tty() if tty_char: - print(chr(tty_char), end="", flush=True) + # print(chr(tty_char), end="", flush=True) + if 32 < tty_char <= 127: + print(f"TTY: {repr(chr(tty_char))} ({tty_char})") + else: + print(f"TTY: ({tty_char:6d}; 0x{unsigned(tty_char):04x})") kvm.update_display(lambda x: computer.peek(SCREEN_BASE + x)) @@ -383,7 +394,7 @@ def main(): print(f"symbols: {symbols}") print(f"statics: {statics}") - run(chip=BigComputer, program=prg, halt_addr=symbols["halt"]) + run(program=prg, halt_addr=symbols["halt"]) if __name__ == "__main__": diff --git a/alt/scheme/repl.py b/alt/scheme/repl.py new file mode 100755 index 0000000..d75b0c2 --- /dev/null +++ b/alt/scheme/repl.py @@ -0,0 +1,30 @@ +#! /usr/bin/env python + +"""Scheme REPL, reading input from the keyboard and writing output to the screen. + +Supported keywords and functions: +TBD +""" + +from alt.scheme import rvm + +def main(): +# pgm = """ +# ;; In lieu of pre-defining additional primitives and re-building rsc, +# ;; with the same result: +# (define poke (##rib 21 0 1)) + +# (poke 16384 21845) +# """ + +# pgm = """ +# (+ 1 2) +# """ + + pgm = "42" + + rvm.run(program=pgm) + + +if __name__ == "__main__": + main() diff --git a/alt/scheme/ribbit/rsc.py b/alt/scheme/ribbit/rsc.py new file mode 100644 index 0000000..0dff701 --- /dev/null +++ b/alt/scheme/ribbit/rsc.py @@ -0,0 +1,87 @@ +input="R1llac/pmuj,htgnel-rotcev,?=rahc,raaadc,_,dna,po-tes,?naeloob,!llif-rotcev,trats-tni-tsnoc,rdaadc,tes,trats-tni-teg,mcl,etouq,dnapxe-dnoc,xam,oludom,esle,trats-corp-tsnoc,radddc,dnoc,rts-ot-tuptuo-htiw,po-teg,cc/llac,adbmal,rdaddc,rdadac,raadac,!tes-rotcev,trats-llac,tel,stropxe-xtc,?>rahc,rotaremun,?tsil,rddddc,roolf,trats-teg,yllamron-margorp-tixe,margorp-daer,ypoc-gnirts,enifed,yllamronba-margorp-tixe,gnirts-ekam,?evitagen,2/be,liat,gniliec,?=gnirts,certel,trats-fi,radadc,!llif-gnirts,tibbir,dmc-llehs,lbtmys,!tes-rav-labolg,?-gnirts,sfed-teser,tsil>-elbat,tros-tsil,enil-dmc,edoc-etareneg,?ddo,rahcteg,tpxe,?regetni,yrarbil-daer,dcg,elif-tupni-htiw-llac,lper,relipmoc-enilepip,htap-elbatucexe,elif-tpircs,ssenevil,sisylana-ssenevil,?=rahc,noisnetxe-htap-csr,htgnel-elbat,gnirts-ot-tuptuo-htiw,dnibnu-neg,enil-daer,!tros-tsil,elif-morf-tupni-htiw,>,lobmys-denretninu>-gnirts,?qe,rebmun>-gnirts,margorp-elipmoc,vssa,?neve,?erudecorp,elipmoc,=<,lave,?>gnirts,redniamer,elif-ot-tuptuo-htiw,?-gnirts,tsil-dnapxe,yrotcerid-htap-csr,xua-rebmun>-gnirts,xua-sisylana-ssenevil,stropxe-htiw-srpxe-pmoc,fer-tsil,raadc,repo,rebmem,rahc>-regetni,xua-gnirtsbus,2xua-rebmun>-gnirts,!tes-tsil,xua-pmc-gnirts,radac,elbat-ekam,xua-rahc-daer,edoc-erudecorp,?=gnirts,xtc-ekam,*dnib-pmoc,?tnatsnoc,!tes-elbat,radc,gnirts>-lobmys,raaac,tnemmoc-piks,tsil-etirw,tsil-daer,?ni,tsila>-stropxe,rdaac,raac,xua-tsil-ekam,rdadc,xua-esrever,raddc,xua-?tsil,evil>-stropxe,elif-morf-gnirts,liat-tsil,tixe,xua-dcg,evil-xtc,lobmys>-gnirts,?2erudecorp,tnatsnoc-dnapxe,hcae-rof,lla-daer,ydob-dnapxe,lave-dnapxe-dnoc,pmc-gnirts,elif-morf-daer,!rac-tes,1tsil,xua-gnirts>-rebmun,lobmys-esu,3tsil,nigeb-dnapxe,rotcev>-tsil,2tsil,?rotcev,rdddc,tsil-ekam,sba,enilwen,ecapsetihw-non-rahc-keep,regetni>-rahc,lobmys-daer,poon-neg,?gnirts,lobmys-denretninu>-rts,tsil>-rotcev,fer-elbat,gnirtsbus,ngissa-neg,tes-etc-xtc,cossa,!tes-2dleif,gnirts>-rebmun,enod-ydob-dnapxe,2rahctup,rdddac,?tcejbo-foe,llac-neg,llac-pmoc,?ecnatsni,daer,fer-gnirts,srahc-daer,etanetacnoc-gnirts,rts>-lobmys,rahc-keep,pp,fi-dliub,?evil,dnetxe,erudecorp-ekam,sesualc-dnapxe-dnoc-dnapxe,dnib-pmoc,etirw,*nigeb-dnapxe,!tes-1dleif,dnpo,etc-xtc,gnirts>-maerts,rorre,?llun,txen,qssa,!rdc-tes,tneitouq,?bir,pukool,evil-dda,srahc-etirw,nigeb-pmoc,=>,esrever,htgnel-gnirts,dneppa,vmem,rddac,=,gnirts>-tsil,!tes-0dleif,xeh-daer,tsil>-gnirts,?lobmys,htgnel,rahctup,*,2dleif,rpxe-dnapxe,yalpsid,hguorht-epip,ecalper-gnirts,setyb-ot-edoc-mvr,rid-toor,?lauqe,pam,dnapxe-htap,rahc-daer,0dleif,1dleif,rddc,pmoc,ton,<,+,-,rdac,esolc,dneppa-gnirts,?riap,rac,?vqe,1gra,2gra,rdc,di,snoc,lin,eurt,eslaf,bir;8U0!U08BYU9YTMYSaZPZ#h-_f7$IlfA^[$G7/fJldb7'Il^~YU+ZB`h1ZBJ`dh/70>>h.ZPh.gh3h4_^Jh/c~Kk^zi$~YTHZ#gJf_|i$Z#aZ#_|!?9@`SYI_G9KYS)^z{!U9(^YA_RUFFiXBeiXI(^^~ATiY&e(^YA_RUFFiXBeiXI(^^~A^~^LgAWiX8iX.WViXHaViXA_WViWNaViX*_Wa_`iXO(^YA_RUFFiXBeiXI(^^~ATiY&e(^YA_RUFFiXBeiXI(^^~A^~^Lg^~TiX0cBi$(^YA_RUFFiXBeiXI(^^~ATiY&e(^YA_RUFFiXBeiXI(^^~A^~^LgAWiX8iX.WViXHaViXA_WViWNaViX*_Wa_`iXO(^YA_RUFFiXBeiXI(^^~ATiY&e(^YA_RUFFiXBeiXI(^^~A^~^Lg^~TiX0cBYBiX9BYBZ#^BYBiY+~Z%ldFiY*(^YA_RUFFiXBeiXI(^^~ATiY&e(^YA_RUFFiXBeiXI(^^~A^~^LgAWiX8iX.WViXHaViXA_WViWNaViX*_Wa_`iXO(^YA_RUFFiXBeiXI(^^~ATiY&e(^YA_RUFFiXBeiXI(^^~A^~^Lg^~TiX0cBi$(^YA_RUFFiXBeiXI(^^~ATiY&e(^YA_RUFFiXBeiXI(^^~A^~^LgAWiX8iX.WViXHaViXA_WViWNaViX*_Wa_`iXO(^YA_RUFFiXBeiXI(^^~ATiY&e(^YA_RUFFiXBeiXI(^^~A^~^Lg^~TiX0cBYBiX9BYBZ#^BYBiY+~Z%ldFYSEe~e_YSERUFFFdiXJbiX$(^YA_RUFFiXBeiXI(^^~ATiY&e(^YA_RUFFiXBeiXI(^^~A^~^LgAWiX8iX.WViXHaViXA_WViWNaViX*_Wa_`iXO(^YA_RUFFiXBeiXI(^^~ATiY&e(^YA_RUFFiXBeiXI(^^~A^~^Lg^~TiX0cBi$(^YA_RUFFiXBeiXI(^^~ATiY&e(^YA_RUFFiXBeiXI(^^~A^~^LgAWiX8iX.WViXHaViXA_WViWNaViX*_Wa_`iXO(^YA_RUFFiXBeiXI(^^~ATiY&e(^YA_RUFFiXBeiXI(^^~A^~^Lg^~TiX0cBYBiX9BYBZ#^BYBiY+~Z%ldFiY*(^YA_RUFFiXBeiXI(^^~ATiY&e(^YA_RUFFiXBeiXI(^^~A^~^LgAWiX8iX.WViXHaViXA_WViWNaViX*_Wa_`iXO(^YA_RUFFiXBeiXI(^^~ATiY&e(^YA_RUFFiXBeiXI(^^~A^~^Lg^~TiX0cBi$(^YA_RUFFiXBeiXI(^^~ATiY&e(^YA_RUFFiXBeiXI(^^~A^~^LgAWiX8iX.WViXHaViXA_WViWNaViX*_Wa_`iXO(^YA_RUFFiXBeiXI(^^~ATiY&e(^YA_RUFFiXBeiXI(^^~A^~^Lg^~TiX0cBYBiX9BYBZ#^BYBiY+~Z%ldFYSEe~e_iX&~TiX(aYTA__@cDb}(!SE8U2G8U&i$^z^z!VO8PYS8`8PYS<~TiY-`YU4^{!U48S8^8S8RRUiY%FiXM^~TiXFYU*^z!S88U$iS<^z!S<'YS<^(i&~ZG^ZCy!>8T=AYU/8T=A^~^YU.y!U>8SaG8LZ$YI^Z?^zZ1XBi&IYG`YGeX?i&hR%7'@^~i$/FFZ1aiX?Z@iX>SaG8LZ$YI^Z?^zZ1XBi&IYG`YGeX?i&hR%7'@^~YT,iX6Z?D^~E^zi$70>h-a@eJlcBYKd^@aYS%iX,7.e@ca~^Z-hR%^D^D_~E_|i$7.e@ca70>h-a@eJlcBYKd^@a@^~^Z-hM^D^D_~E_|i$70>h-`@eJlcBYKd`YS%iWP70>h-`@eJlcBYKd`@^~^Z-hH_@_D^D_~i$7&ca_A^[$G7&cg_A^[$G7$aA^[$G/FFZ1aiX?Z@iX>SaG8LZ$YI^Z?^zZ1XBi&IYG`YGeX?i&hR%7'@^~i$/FFZ1aiX?Z@iX>SaG8LZ$YI^Z?^zZ1XBi&IYG`YGeX?i&hR%7'@^~YT,iX6Z?D^~E^zi$70>h-a@eJlcBYKd^@aYS%iX,7.e@ca~^Z-hR%^D^D_~E_|i$7.e@ca70>h-a@eJlcBYKd^@a@^~^Z-hM^D^D_~E_|i$70>h-`@eJlcBYKd`YS%iWP70>h-`@eJlcBYKd`@^~^Z-hH_@_D^D_~KiV4^~E_|i$YU;YUciUMZ3`Z._~CiV.^7+AZ0_iX@7+AX/c^~YS?^7+AX3dX4_iV6~YH^7+AX4d_iW@7+A>cJ_iV,~Kv.^~X-^Z3`Z._~CiWH^7+AZ0_iXG7+AX3dX4_iVE~YH^7+AX4d_iW=7+A>cJ_iW#~Ku^~X-^Z3`Z._~CiW2^7+AZ0_iXN7+AX3dX4_iV&~YH^7+AX4d_iVA~X-^Z3`Z._~CiWC^7+AZ0_iY(7+AAX4e_iVB7+AA>dJ_iW+~KiV4^X3^~YH^7+AX4d_iWJ~X-^Z3`Z._90_iY)73d_iV<'cJ_k~Kv7^X2^~YH^73c_iUP~X,^Z3_~CZ.`k~CiUA^YT7^~Z*^{[&G8U5^z['G7,X4d`JoiW67,>cJ`iW6~Ko_Z._P^YT-^{[(G72e_`(^~YMk`>bJiVI^72e_`(^~YMk`>b^~CcaIYEiVI__Z+iVI^|[)G7.a`^{[*G'``'@`Jl`~YMJliVI^D^X/a_|[+G'X0b`^|[,G89^YS#i$_h>z[-G(i$76n^~CiWC_77l^BZ4_aBYKiW2aX8^76m^~AAi$77l^BZ4_aBYKiW2aX8^76m^~AAZ%k`~X-`77l^BZ4_aBYKiW2aX8^76m^~AA^~^YS?_77l^BZ4_aBYKiW2aX8^76m^~A^~^YH^~CiWH_76l^~CiW2_76k^~CiUA_73^~CiV._Z3_YT7^z[.G(i$71Z.^BX0^~Z*^z[/G(i$75^~YS?^(i$9LJYD`l^~YMm`94JO`l^~YMl`8KJP`l^~YMk`AA^BYT(`ahA:kkk(i$9LJYD`l^~YMm`94JO`l^~YMl`8KJP`l^~YMk`A^~^YS#i$_h?~YH^{[0G(^BX4`^{[1G71Z.YT-^z[2G(^BX4k^z[3G(^BX6n^BX?_`[9Jlh8YS@FZKh8iX#1^~^Z-h>^(w&~Ci&^(w%~Ci%^(w$~Ci$^z[4k[5G9LX=X;YD_^P^z[6G7%`h>A^[$G(_7-XE:gbiWC_@bN`H_D^D^~E^{i$z[7G#X>bYD`O_P^(_~Ck^{[8G7%a@iX-A^[$G(_7)AAb7)AAX-c_~i$7)AAb7)AAX-c_~AAKYDak7)AAb7)AAX-c_~AA^~^KO`k7)AAb7)AAX-c_~A^~^KP_k~^YS#i$_hFDD_@^~E^{i$[$G#::::h.XBngiWCX@kw#iUAliWHkiWHHZ-iX-_iWH{i$z[9G(`[@>h@>>`a_X?k^{[:G90_iX/7@XA::fX;kw#iUAoiWHYG_^YS$^~YS/^7@XA::fX;kw#iUAniWHYG_^SYI_iS)~YS&^7?X@::eX:kw#iUAkiWH@_D^~E^#a_iWH#::eX:kw2iUAIbkiWHkiWH~Kk^~X*^#aX6m_iWH~YH^#bX7l_iW2X7^~AZ-h@_90_iX/7@XA::fX;kw#iUAoiWHYG_^YS$^~YS/^7@XA::fX;kw#iUAniWHYG_^SYI_iS)~YS&^7?X@::eX:kw#iUAkiWH@_D^~E^#a_iWH#::eX:kw2iUAIbkiWHkiWH~Kk^~X*^#aX6m_iWH~YH^#bX7l_iW2X7^~A^~^YOiX%^{[;i&[@``^~^Z;h1_Bi$(i$70i$d_BZ,>@``^~^Z;h1_BX/_~h17/i$c^~YSOc_YN`H_~CwV%^7-^H_~CwW;^D^~E^7,^(i$~YSO`^~YH^|[&G(i$8S=YS$_c~YS/^7)@^BX)D^~E^7*^~YH^z['G(k[,Z(g^zi$i$i$i$|!SO5CZ)ka_^{!T)(i$(i$(i$,DH_wW;~EH^~Z/N^~E@^z];(i$9;@a_(^~CD__D_~E_{]('a^>i&^(_~Z;`^{!U=7$_A^[$G(i$7'@^BZ,i&D^~E^zi$z!SF(i&'YSF@_>i&DD^~E^z!T;7$Z(Z(Z(Z(YSFAi&7$Z(Z(Z(Z(YSFA^~^dw(w0w*w+A^[$G7(^(_~C`^YU-Le_bBYU=^zi${!U,(^8T;a_~ZM_wUHYT;i&^{!T>(i&'YT>@_YCD^~E^z]8(_98a@_95a@^~AYS:D_98a@_95a@^~A^~^CD_wW7D^~E^{!S:,wUJ^5LYOS@`iS:i%~i$,wUJ^5LYOS@`iS:i%~CwV5D^~E^5YOS@`iS:i$~i$,wUJ^5LYOS@`iS:i%~i$,wUJ^5LYOS@`iS:i%~CwV5D^~E^5YOS@`iS:i$~CwWDD^~E^5YS:H^~i$,wUJ^5LYOS@`iS:i%~i$,wUJ^5LYOS@`iS:i%~CwV5D^~E^5YOS@`iS:i$~i$,wUJ^5LYOS@`iS:i%~i$,wUJ^5LYOS@`iS:i%~CwV5D^~E^5YOS@`iS:i$~CwWDD^~E^5YS:H^~Cw5D^~E^z]5(_'_YC_98_@_~i$'_YC_98_@_~CwW:D_~E_95_@_~i$'_YC_98_@_~i$'_YC_98_@_~CwW:D_~E_95_@_~CwV'D_~E_Z5a@_D^~E^{!S28S>k-^'_wV'~E@^~E^Z5i&^z]J8S2_8C>>aZ$_wUN~E^{!S;7%i&_A^[$G9JiX<_9J``7+>c>Na_@`7+>c>>i&>>Nc@awW0D_@`~E^H^~i$9J``7+>c>Na_@`7+>c>>i&>>Nc@awW0D_@`~E^H^~i$9J``7+>c>Na_@`7+>c>>i&>>Nc@awW0D_@`~E^H^~E@^~CD_wVM~E^D^~E^{i$z!S>'>i&_wW;z!C8S>^8T>_8Ci$8C>>>>i&>NcwW4>@HbwV'DH`wU?8C>@H`wV'~CDH`wW7~E@_~CwW4^8Ci$8CH_8C>>>i&>>>>i&>NewV5wWEwWEwU?>i&>>i&HawWEwW*~EN_~E@_~CwV5^8Ci%8CH_8C>>>>i&i$>NbwWDH`wU?~EN_~E@_~CwWD^'>>i&YCYNb_wV%'>>i&YC>>Nd@awW0D_wV%~E^H_~CwVM^8S2@_~CwV'^8C>>YPNcSaG'>>i&H`D_wV%zS`G'>i&i$D^zwW*H_~CwUN^8C>@a8C>>>i&>>Nd@awV)>i&D_~i$8C>@a8C>>>i&>>Nd@awV)>i&D_~E@_~E_wW*H_~CwV)^8S;Na'>>i&YS;NcS`G'>i&YCH_D^zwW*~E^^8CA>S`i1>>>i&a>i&>>i&>>YS.eSbi-wW0`wUNYN`~YH^H_~CwW*^'>>i&YS;Nb_wW0H_~CwW0^'>>>i&i$'>>>i&YCZHb~EYS.bYCYNaYCH`wU?~CwU?^'>>i&YCYNb_wV%H_~CwV%^8S>H_~CwW;^D^~E^(^~YH^z!TM(^Bi$(^BZ=@^BYBiX3~Z%nbBi$(^Bi$(^BZ=@^BYBiX3~Z%nbBZ=D^BYBiXK~Z%mbYT:YSN_>i&i$(^Bi$(^BZ=@^BYBiX3~Z%nbBi$(^Bi$(^BZ=@^BYBiX3~Z%nbBZ=D^BYBiXK~Z%mbYT:YSN__~E_@_D^YT@_{!T:'ASaG'_^D^z'A^~^bZ9i&:MiVHbYT+Ai&'ASaG'_^D^z'A^~^bZ9i&:MiVHbYT+A^~^eai&kkYU,a^YS2^{!SN(^8<_G'H_D^'_^~YH^z~E^z!T@7&i$i&_A^[$G'aZ$_7,c>b_@_7,YPAi&7,YPA^~^d@`a@_~i$7,c>b_@_7,YPAi&7,YPA^~^d@`a@_~CwV>D^~E^D^~E^|i$z!VH:kw(iUA]:(_'Z:a@_D^~E^{])(^9)Jlb@`^(`~C_D_~E_|]E7&^6ZEd@bZN>Z2bi$`D`^~E_|]&6b6:Z&f@dbYS4w+aiUA~E@aD`^|!S4(_BYTBZ(YSA``^{!U'#aYS4w*_iUA(_~CiVH_{!T*9&eca6YT*YU'h2gh/ZN>Z2h/eh-@f@dZN>Z2di$b_`DaD_~E_})]78T*geab`^}(!S'#`kiWH8D^~i$#`kiWH8D^~i$#`kiWH8D^~i$#`kiWH8D^~Z*YD^~Cw+O^~CiUAP^~Z*^z]O#YS'a_iWC{]F#a_iUA#k_iUA~CiVH_{!6#b`iWH97f>i&>bwWE>i&aiXD`9EG9Fh.^Z)kZ2_dz_`~YH_@`97gaSbi1Sai-aNaH`~CwW*^9&c@a_~CwV'^#ZFeYS4w0b#d~Z/Z2bZ9i&:Z&iVHNeZNZ:>>Z2gi$i$bckYG_iWHH`~CwW0^6`Hdb:a_iV.MdYNb`McZHa_~CwU?^6ZOg``b8S'e6ZOh-aac8S'e~YT)^~^Z;YSAc`~Ca^Z)lZ2b_YNaH`~CwV%^#cHaiWH~CwW;^D_~E_#d`iW2#dHH_iWH~i$#d`iW2#dHH_iWH~YT)^~^Z;YSA``Z)kZ2__~YH_|!TB8S7`O^{]N#YD`O__{!W)8D^z!SA-O^z]289^z!T+#b>i&`^|].8D^z]388^z!T789^z!S?iTJ!V.wU?!WHwV(!W2wV*!WCwW>!UAwWM]@(iY'7%YIDa@`A^[$G8L_7)YPYPbeYID_@^~E^{i$YI`Z$^~E^{!U/(iXPy!U.-YU:y!U;8U%YPi&`^{!U%7(_c(i&~YM_kYGb[$G(^BZ,b_(^BZ,a_7/f@dc`BZ,ca7/@fdd`BZ,da~X0`^DbD`~Ea~E`}'[%G(_(^7-d@ba`7-@dbba~X.`^D`D^~E_~E^{[&G7%a_A^[$G71_X2eeX1Ief_7)@`Il^~YU#k^{i$Z+m_(^BZ,i&^~YMl_{i$i$i${]=8S+BZ6^z!U&7$i&A^[$G7(>`^8LZ$_~ACf_7(>`^8LZ$_~A^~^ZG^Qbzi${!;/F`iX+_/__~YMvR$YS)ZBIlZ#`_(^~YMkZ#_{!T=7$IlZ#_A^[$G7'Il^9PJl`kb~YMvR$YS)ZB_b(iWO~Kk^zi$z!U*7$IlZ#_A^[$G7'Il^9PZ#d_b~YMvR#YS)ZB_b(iX1~Kk^zi$z]?8S#YT&`_iV9z!S%(^BYT(b_iV9YS@^FZKYU)iV9iX:z!V9YT/!U<-^z!U)8GD^z!T(8S7>Da>ca_9,b^~^ZMD__|!S#(a)^~^ZMD__|!T/'i&i&y!W38U(^z!VL8SCly!VP8SCky!U:'i&iY,y!A(_BYBiX'BYB^BYBiXLBYBiY$BYBiX={!VC(i$z!UI(i$z]08SClBYS+BZ6_BYBiXEBYB^{!U18U1BYS+BZ6YTG^8S+~ZG^ZCBZIvCvR3y!TG7#YTI^z!TI99i&:MiVHai&kkz!VH:kw(iUA]:(_'Z:a@_D^~E^{])(^9)Jlb@`^(`~C_D_~E_|]E9F`^Z)ka_@aD`6ZEd@b>ai$D`^~E_|!S'#`kiWH8D^~i$#`kiWH8D^~i$#`kiWH8D^~i$#`kiWH8D^~Z*YD^~Cw+O^~CiUAP^~Z*^z]O#YS'a_iWC{]F#a_iUA#k_iUA~CiVH_{]&6b6:Z&f@dbw+iUA~E@aD`^|]76Z&:h-w*iUA6Z&f~CiVHfd>aaa^}(!S6'i&^z!S0'YS6`^{!S3'YS0b`^|]<'YS3ca_wU?|!6#b`iWH97f>i&>bwWEawWE`9E>ea_`~YH_@`6ci$6cZ<>NdwW4>@HcwV'DHa6c>@HbwV'~CDHbwW7~E@a_~CwW4^6ci$6cA^6cAYS3Z<>NgwV5wWEwWEYS6YS0`wWEwW*~ENbHa~E@a_~CwV5^6ci%6cA^6cAZNdwWD^~ENbHa~E@a_~CwWD^97fNdH`D_`DH`~CwW*^9&c@a_~CwV'^#ZFew0#d~Z/bZ9i&:Z&iVHNeZ:>>fi$i$akYG_iWHH`~CwW0^6:MgZHecMfYNdbiV.Ha_~CwU?^6ZOdZ)lbHbYNa_~ACwVM_97f>i&>bwWEawWE`9E>ea_`~YH_@`6ci$6cZ<>NdwW4>@HcwV'DHa6c>@HbwV'~CDHbwW7~E@a_~CwW4^6ci$6cA^6cAYS3Z<>NgwV5wWEwWEYS6YS0`wWEwW*~ENbHa~E@a_~CwV5^6ci%6cA^6cAZNdwWD^~ENbHa~E@a_~CwWD^97fNdH`D_`DH`~CwW*^9&c@a_~CwV'^#ZFew0#d~Z/bZ9i&:Z&iVHNeZ:>>fi$i$akYG_iWHH`~CwW0^6:MgZHecMfYNdbiV.Ha_~CwU?^6ZOdZ)lbHbYNa_~A^~^CwV%^#cHaiWH~CwW;^D_~E_#bZ)k``iW2~YH_|!V.o!WHn!W2m!WCl!UAk]I8F_BYF^{!S+8Fuy!UEiF]'(i$9'a@_BYF^9'a@_BYF^BYFvS#~ACvS#_9'a@_BYF^9'a@_BYF^BYFvS#~A^~^CvE^9'a@_BYFvS;BYFvS#~Ct^9'a@_BYFvS9BYFvS#~Cv0^9'a@_BYFvS5BYFvS#~Cu^9'a@_BYF^~L`D^~E^{!T#(i$(i$8T#@^BZ6D^~E^BYFvC~E^z!B8BZK^9IvS7vF~YTJ^96YS$^BYFvF~YS/^9'i$YI^~YS&^8BYT&^~YH^8FvLBYT#@^BZ6D^BYFvK~E^9IvLvK~Z/^9IvS;vF~Ci%^9IvS-vF~L^z]68B^8FvEBZ'i%YI^BYFvE~YS&^z!T$8T$8S*~Cu^(^~Kk^Qy!S*8S*BQ(^8T$~CvR0^~K_vC(iX5~ZG^Z>y]A9A>`^9A>a^9A>at~CvS;^9A>av0~CvS9^9A>au~CvS5^Q~CvS#^9$_~CvE^(i&~ZG^Qz!S('YS(^BQ(i&~AAKvD`'YS(^BQ(i&~AA^~^CvL_'YS(^BQ(i&~A^~^CvK^Z>y!SP'YSP^ZC(i&BQ~CvL^YS*y!J(_8JIIvRL_YE`v3BQ~i$(_8JIIvRL_YE`v3BQ~KvS.^~K_vS'8JIIvR,_YE`v3BQ~i$(_8JIIvRL_YE`v3BQ~i$(_8JIIvRL_YE`v3BQ~KvS.^~K_vS'8JIIvR,_YE`v3BQ~KvR<^~K_vR58JIIvR%_YE`v3BQ~i$(_8JIIvRL_YE`v3BQ~i$(_8JIIvRL_YE`v3BQ~KvS.^~K_vS'8JIIvR,_YE`v3BQ~i$(_8JIIvRL_YE`v3BQ~i$(_8JIIvRL_YE`v3BQ~KvS.^~K_vS'8JIIvR,_YE`v3BQ~KvR<^~K_vR58JIIvR%_YE`v3BQ~KvR/^~K_vR$Z>z]C8S@`(^~^^YTN^YL>YS(^BQ8LZAi&BQ~CvE^'>i&ZCwW;BQ~CvJ^8S1ZC2YJkk8JkBQ~CvP^Z>BQ~ACvRM_8S1ZC2YJkk8JkBQ~CvP^Z>BQ~A^~^CvS?^(i%BQ~CvS;^(i$BQ~CvS-^Z>BQ~CvF^8SPBQ~CvK^(^~Kk^YS*y]>(^!V3^Qy!T.(^!V3iX4(^~CiX5^!V3^z!:8T.^8T.YU7~CiX4^(^~CiX5^iV3y!V3iX4]G,iX5^z!W17%G(_BZLYDc^BYKPc^OOGi$zOOGi$z!S=(i$8S=@`^BX$D_~E_{!<(i&'S@a_X$D_~E_{!V#i8!T-i9]9#l`^{!TJZDl!WA8KYS-aO_^{!V28S1YS-k^z!W,8T2b`P^|!U@8T9`P^{!WLi8!S$i9!S1#oYG_^z!S/ZDo!UK8KYS-aO_^{!VN8LYPi&YI^z!/8LYPYI`YI^{!T48La8T4>fZBbb`a_Il`~Ka_}']P8T4i&b`^|!T1(k(iX5~E_(l8T1@b@`(l~K`^(iX5~K__D`D^~E_~E^{!S98T1YI`YI^{!UO5YTC`^{!WK5YTF`^{!TF4YS9a_k{!TC4kYS9`^{!T,,kYS9`^{!VK8LYS-vC^z!V18T2b`P^|]B8T9`P^{]#i8!Ii9!L#nYG_^z!S&ZDn!T5i(!S)i(!WGj%!VFiTH!W(iU#!UFi4!U+i,!T3(_(i$(i$8T3IIvR%`YEbu@_~KvR/^~K_vR$D^~E^{!T<8T3k^(i$~Z/^z!TN(i$2_k~^YT<^8T<@^~CvPD^(i$~Z/^YI^z!S5(^8S5_`~Kak>b^JIYEu``vR%Z+u^{]K8LYS5i&^8L>YS5i&I`kvP~Kk^z!U6(^8E__~YU8`YU6Z+m`YE_^(l~Ck_{!VDi(!V;i(!VGi(!W$i(!V?(lz!W'i(!W<8E_Z+YU3``_YS,`YS,^(k~Ck_{!SB8SB_YTE__(_~Ck^{!U38SB`^8SB__~K__YS,`YS,^{!W83b^(^~CKkbKk`(k~Ck^IYE`a_Z+`^{!TE2YEZ+b``^{!S,(^2_k~Kk^z!V@(_(^~K`^{!W9(^(_~K`^{!U85YTK^z!TK,YEZ+m`m^z!VJ4k^z!UD4_kz!UC,k^z]%5K`^{!TH5K__{!U#4__{!Mi,!U55Z*^zBZ4ki#!UHOi#!T?(^!UH>iUH^YTP^8T?Oa_(^~T`O^P_~E_{!S@8T?iUH^z!UGiK!V/i9!T&i8!TP#m_i$z!HZDm!SK(`8SK>ca`Il^~K_k|!S-8SKi&`^{]M(i$9M@a_(^~TD__D_~E_{]-iTL!TL(i$8TL@a_(^~CD__D_~E_{!T6(i$8T6@`^(_~TD`^~E_{!V0iO!O(i$8O@`^(_~CD`^~E_{!SD(^8SDIl`@^~K`k{!T28S7aYSD`^|!T9-YSD`^{!SI(_8SI>aD_@^~E^{]$8SIi&^z!P(_'YPa@_D^~E^{!G(k3YG@_l~E^z!SG9/^9/^8SG@a@^~E^(i$~YTOa^@^~E^{!W&8SG_^z]/,i&^z!W%8S.O^z!W58S.P^z!W/8SHO^z!V-8SHP^z!V+8SJO^z!UL8SJP^z!W?8T8O^z!WF8T8P^z!V88NP^z!W.8T0O^z!W-8T0P^z!UB8SMO^z!V:8SMP^z!V78T%O^z!WI8T%P^z!S.87O^z!SH87P^z!SJ8T'O^z!T88T'P^z!T01P^z!SM8SLO^z!T%8SLP^z!T'88P^z!SL89P^z]H8NO^z!N1O^z!788O^z!189O^z],j4!S7iK!)i8!-i9!'#k`^{!.ZDk!=(i$(i$(i$(i$8=PaP_~TOaO_~TYDaYD_~Z*`(i$~CpYD_~Z*_(^~^C`^{!TOi,!WB5_(^~^Ci%^z!5,i$^z]D0(i$,bYD^~Z*^zz!XC:nn:k:k:ki&vS4vS=vS9!X;:nl:ki&vP!Y#:nki&!XJ:np:k:k:k:k:ki&vR#vS4vS=vS9vR$!X$:np:k:k:k:k:ki&vR$vS;vS:vS6vS/!Y*:nki&!X8:nv::k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:ki&vS4vS(vS9vS.vS6vS9vS7vCvS,vS/vS;vCvS-vS6vCvS,vS+vS6vS*vCvRBvRKvRG!X.:nv>:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:ki&vDvRDvRAvRAvR:vR=vCvS:vS;vS5vS0vS9vS7vCvS;vS(vS/vS;vCvS,vS+vS6vS*vCvRBvRKvRG!XH:nl:ki&vO!XA:nl:ki&vO!WN:nl:ki&vC!X*:nl:ki&vC!XO:nvR$:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:ki&vS@vR+vS=vS2vS3vR/vJvDvS4vS2vS3vR/vKvDvR2vRGvS=vR3vR4vR/vRGvS=vR3vR4vR6vRGvS=vR3vR4vR6vRGvS=vR3vR4vR9vRGvS=vR3vR4vR9vS=vR3vR4vS:nl:ki&vO!X6:nki&!X,:nki&!WP:nki&!X):k:k:k:ki&w&w%w$w#!X2:nv/:k:k:k:k:k:k:k:k:k:k:k:ki&vS+vS,vS;vS*vS,vS7vS?vS,vCvS)vS0vS9!X7:nu:k:k:k:k:k:k:k:k:k:ki&vS7vS6vCvS5vS>vS6vS5vS2vS5vSvCvS;vS/vS.vS0vS4vCvSvCvS,vS+vS6vS*vCvS+vS,vS;vS(vS9vS,vS5vS,vS.vCvS,vS/vS;vCvS6vS:vCvMvMvM!X=:nvR/:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:k:ki&uvS4vS,vS;vS:vS@vS:vCvS,vS4vS,vS/vS*vRHvCvS:vS0vS/vS;vCvS/vS;vS0vS>vCvS+vS,vS;vS9vS6vS7vS7vS0:b-=1;h=[[q,[r,0,3],2],h,0] +t=r +b=0 +while 1: + e=I() + if e==44:h=[[q,[t,b,3],2],h,0];t=r;b=0 + else: + if e==59:break + t=[e,t,0];b+=1 +h=[[q,[t,b,3],2],h,0] +J=lambda b:C(h,b)[0] +while 1: + a=K();b=a;p=0;k=0 + while 1: + p=[20,30,0,10,11,4][k] + if b<=2+p:break + b-=p+3;k+=1 + if a>90:b=g() + else: + if k==0:d=[0,d,0];k+=1 + b=x(0)if b==p else J(x(b-p-1))if b>=p else J(b)if k<3 else b + if 4= len(input)-1: break # TEMP + + x = get_code() + asm.comment(f"x = get_code() = {x}") + n = x + d = 0 + op = 0 + + while True: + d = [20, 30, 0, 10, 11, 4][op] + if n <= 2+d: break + n -= d+3; op += 1 + + asm.comment(f"op: {op}; n: {n}") + + if x > 90: + n = pop() + asm.comment(f"[if] x: {x}; n = {n}") + else: + if op == 0: + push(0) + stack = [0, stack] + asm.comment(f"op == 0; TODO: stack = cons(0, stack)") + op += 1 + + if n == d: + n = get_int(0) + asm.comment(f"n = get_int(0) = {n}") + elif n >= d: + #n = symbol_ref(get_int(n-d-1)) + idx = get_int(n-d-1) + asm.comment(f"idx = get_int({n-d-1}) = {idx}") + n = f"symbol_ref({idx})" + asm.comment(f"{n} >= {d}; TODO: n = symbol_ref({idx})") + elif op < 3: + # n = symbol_ref(n) + n = f"symbol_ref({n})" + asm.comment(f"{op} < 3; TODO: n = {n}") + + if op > 4: + # n = rib(rib(n, 0, pop()), nil, 1) # a proc, obviously + m = pop() + n = [[n, 0, m], "@rib_nil", 1] + asm.comment(f"proc: {n}, ...") + if not stack: break + op = 4 + + # FIXME: this is making a rib... + asm.comment(f"TOS = rib({op-1}, {n}, {stack[0]})") + # stack = ([op-1, n, stack[0]], stack[1]) + push([op-1, n, pop()]) + + # FIXME: initialize PC + # This definitely needs to happen at runtime, not here + asm.comment(f"TODO: pc = n[0][2] = {n[0][2]}") + + + +def interpreter(asm): + """ROM program implementing the RVM runtime, which interprets a program stored as "ribs" in ROM and RAM.""" + + # Interpreter variables in low memory: + PC = 0 + SP = 1 + NEXT_RIB = 2 + + # RETURN = 3 + + # used only during initialization? + SYMBOL_TABLE = 4 + + num_primitives = 22 + FIRST_PRIMITIVE = 8 + LAST_PRIMITIVE = FIRST_PRIMITIVE + num_primitives + + FIRST_RIB = big.HEAP_BASE + + FALSE = "rib_false" + TRUE = "rib_true" + NIL = "rib_nil" + + # FIXME: probably no return; jump straight to exec_loop? + # def call_primitive(lbl): + # rtn_label = asm.next_label("return") + # asm.instr(f"@{rtn_label}") + # asm.instr("D=A") + # asm.instr(f"@{RETURN}") + # asm.instr("M=D") + # asm.instr(f"@{lbl}") + # asm.instr("0;JMP") + # asm.label(rtn_label) + + # def return_from_primitive(): + # asm.comment("return") + # asm.instr(f"@{RETURN}") + # asm.instr("A=M") + # asm.instr("0;JMP") + + def push_d(): + """Push the value in D onto the stack, without over-writing it.""" + asm.instr(f"@{SP}") + asm.instr("M=M-1") + asm.instr("A=M+1") # i.e., point to the previous location + asm.instr("M=D") + + # def rib(x, y, z): + # asm.comment(f"rib: {x}, {y}, {z}") + # asm.comment("TODO: D = {x}") + # asm.instr("@{NEXT_RIB}") + # asm.instr("A=M") + # asm.instr("M=D") + # asm.comment("TODO: D = {y}") + # asm.instr("@{NEXT_RIB}") + # asm.instr("AM=M+1") + def rib_append(val="D"): + """Add a word to the rib currently being constructed. + + Always called three times in succession to construct a full rib. + The value is either "D" (the default), or a value the ALU can produce (0 or 1, basically). + """ + + asm.instr(f"@{NEXT_RIB}") + asm.instr("M=M+1") + asm.instr("A=M-1") + asm.instr(f"M={val}") + + + # TODO: find end of encoded program; initialize stack pointer below it + decode_stack_start = 16384//2 - 1 + asm.comment("SP = below the encoded program") + asm.instr(f"@{decode_stack_start}") + asm.instr("D=A") + asm.instr(f"@{SP}") + asm.instr("M=D") + asm.comment(f"NEXT_RIB = {FIRST_RIB} (HEAP_BASE)") + asm.instr(f"@{FIRST_RIB-1}") + asm.instr("D=A+1") + asm.instr(f"@{NEXT_RIB}") + asm.instr("M=D") + + # prim_rib_label = asm.next_label("prim_rib") + + # asm.comment("Initialize primitive vectors") + # for i, l in enumerate([prim_rib_label]): + # asm.instr(f"@{l}") + # asm.instr("D=A") + # asm.instr(f"@{FIRST_PRIMITIVE + i}") + # asm.instr("M=D") + + # asm.comment("Initialize shared constants") + # for n in ("false", "true", "nil"): + # asm.comment(f"{n} = rib(0, 0, 5)") + # asm.instr("D=0") + # push_d() + # push_d() + # asm.instr("@5") + # asm.instr("D=A") + # push_d() + # call_primitive(prim_rib_label) + + asm.comment("Construct the symbol table in RAM:") + asm.comment("SYMBOL_TABLE = '()") + asm.instr("@rib_nil") + asm.instr("D=A") + asm.instr(f"@{SYMBOL_TABLE}") + asm.instr("M=D") + + asm.comment("R5 = ptr") + asm.instr(f"@symbol_names_start") + asm.instr("D=A") + asm.instr("@R5") + asm.instr("M=D") + + loop = "symbol_table_loop" + asm.label(loop) + + asm.comment("DEBUG: log the pointer to tty") + asm.instr("@R5") + asm.instr("D=M") + asm.instr("@KEYBOARD") + asm.instr("M=D") + + asm.comment("new symbol, with value = false") + asm.instr("@rib_false") + asm.instr("D=A") + rib_append() + asm.instr("@R5") + asm.instr("D=M") + rib_append() + asm.instr("@2") # symbol + asm.instr("D=A") + rib_append() + + asm.comment("SYMBOL_TABLE = new pair") + asm.comment("car = the rib we just wrote") + asm.instr(f"@{NEXT_RIB}") + asm.instr("D=M") + asm.instr("@3") + asm.instr("D=D-A") + rib_append() + asm.comment("cdr = (old) SYMBOL_TABLE") + asm.instr(f"@{SYMBOL_TABLE}") + asm.instr("D=M") + rib_append() + rib_append("0") # pair + asm.instr(f"@{NEXT_RIB}") + asm.instr("D=M") + asm.instr("@3") + asm.instr("D=D-A") + asm.instr(f"@{SYMBOL_TABLE}") + asm.instr("M=D") + + asm.comment("DEBUG: log NEXT_RIB to tty") + asm.instr(f"@{NEXT_RIB}") + asm.instr("D=M") + asm.instr("@KEYBOARD") + asm.instr("M=D") + + asm.comment("increment R5") + asm.instr("@R5") + asm.instr("M=M+1") + + asm.comment("D = compare(R5, symbol_names_end)") + asm.instr("D=M") + asm.instr("@symbol_names_end") + asm.instr("D=D-M") + asm.instr(f"@{loop}") + asm.instr("D;JLT") + + + # decode_label = "decode_loop" + # asm.label(decode_label) + # asm.instr(f"@{decode_label}") + # asm.instr("0;JMP") + + # # Now move the stack pointer to the top of memory (over-writing the encoded program) + # asm.comment("SP = top of memory") + # asm.instr("@16384") + # asm.instr("D=A") + # asm.instr(f"@{SP}") + # asm.instr("M=D") + + asm.comment("PC = ?") + asm.comment("TODO") + # asm.instr("D=?") + asm.instr(f"@{PC}") + asm.instr("M=D") + + # + # Exec loop: + # + + exec_label = "exec_loop" + asm.label(exec_label) + asm.comment("TODO") + asm.instr("D=D") + # asm.instr(f"@{exec_label}") + # asm.instr("0;JMP") + + + # + # Halt: + # + + halt_label = "halt_loop" + asm.label(halt_label) + asm.instr(f"@{halt_label}") + asm.instr("0;JMP") + + # asm.label("prim_rib_label") + # asm.comment("TODO:") + # asm.comment("- check for stack-heap collision") + # asm.comment("- allocate space for new rib") + # asm.comment("- pop three values and copy them into the new rib") + # return_from_primitive() + From 0d739b748f1fea1a92a26eceba0b0f7f680e3af4 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sat, 27 Jan 2024 13:08:18 -0500 Subject: [PATCH 030/221] Possibly legit decoding of instructions --- alt/scheme/rvm.py | 159 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 110 insertions(+), 49 deletions(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index eb8ff71..3a78f1a 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -48,11 +48,11 @@ def run(program): runtime_words = len(instrs) - big.ROM_BASE total_rom = big.HEAP_BASE - big.ROM_BASE - # TODO: count Ribs in the ROM + # TODO: count ribs in the ROM separate from the fixed runtime # program_words = len(encoded) reserved_words = 8 + 22 # Interpreter/decoder state, and the primitive vectors # available_ram = 0x4000 - reserved_words - print(f"Runtime: {runtime_words:,d} ({100*runtime_words/total_rom:0.2f}%)") + print(f"ROM: {runtime_words:,d} ({100*runtime_words/total_rom:0.2f}%)") # print(f"Encoded program: {program_words} ({100*program_words/available_ram:0.2f}%)") print() @@ -77,7 +77,7 @@ def trace(computer, cycles): next_rib = unsigned(computer.peek(2)) current_ribs = (next_rib - big.HEAP_BASE)//3 max_ribs = (big.HEAP_TOP - big.HEAP_BASE)//3 - print(f" heap: {current_ribs} ({100*current_ribs/max_ribs:0.1f}%)") + print(f" heap: {current_ribs:3,d} ({100*current_ribs/max_ribs:0.1f}%)") big.run(program=instrs, @@ -177,9 +177,11 @@ def get_int(n): # return n+x if x < 46 else get_int(n + x - 46) - def emit_rib(lbl, x, y, z): + def emit_rib(lbl, x, y, z, comment=None): if lbl: asm.label(lbl) + if comment: + asm.comment(comment) asm.instr(x) asm.instr(y) asm.instr(z) @@ -200,7 +202,7 @@ def emit_vector(lbl, elems, count: int): emit_rib(lbl, elems, f"#{count}", "#4" # First byte(s): number of symbols without names n = get_int(0) - sym_names = n*["rib_string_empty"] + sym_names = n*[("rib_string_empty", "")] accum = "@rib_nil" acc_str = "" @@ -215,7 +217,7 @@ def emit_vector(lbl, elems, count: int): emit_rib(lbl, elems, f"#{count}", "#4" lbl = f"rib_string_{idx}" emit_string(lbl, accum, len(acc_str)) idx += 1 - sym_names += [lbl] + sym_names.insert(0, (lbl, acc_str)) accum = "@rib_nil" acc_str = "" @@ -234,13 +236,17 @@ def emit_vector(lbl, elems, count: int): emit_rib(lbl, elems, f"#{count}", "#4" # block of address space? asm.comment("Table of pointers to symbol name ribs in ROM:") asm.label("symbol_names_start") - for lbl in reversed(sym_names): + for lbl, s in sym_names: + if s != "": + asm.comment(f'"{s}"') asm.instr(f"@{lbl}") asm.label("symbol_names_end") # Decode RVM instructions: + asm.comment("Instructions:") + stack = None def pop(): nonlocal stack @@ -250,11 +256,48 @@ def push(x): nonlocal stack stack = (x, stack) + def symbol_ref(idx): + """Statically resolve a reference to the symbol table, to an address in RAM where that + rib will be allocated during initialization. + + The table is written from the end, and each entry is made of of two ribs, the `symbol` + and a `pair`. + """ + # TODO: fix the inevitable of-by-one error(s) here + asm.comment(f'symbol_ref({idx}); "{sym_names[idx][1]}"') + return f"#{FIRST_RIB + 6*(len(sym_names) - idx)}" + + def emit_instr(op, arg, next): + lbl = asm.next_label("instr") + + asm.label(lbl) + + if op == 0 and next == "#0": + asm.comment(f"jump {arg} ") + elif op == 0: + asm.comment(f"call {arg} -> {next}") + elif op == 1: + asm.comment(f"set {arg} -> {next}") + elif op == 2: + asm.comment(f"get {arg} -> {next}") + elif op == 3: + asm.comment(f"const {arg} -> {next}") + elif op == 4: + asm.comment(f"if -> {arg} else {next}") + else: + raise Exception(f"Unknown op: {op} ({arg}, {next})") + + asm.instr(f"#{op}") + asm.instr(arg) + asm.instr(next) + + return lbl # For each encoded instruction, emit three words of data into the ROM: # - references to symbols are statically resolved to addresses in *RAM*, # where the references + # TODO: reverse the instruction stream so it reads *forward* in the commented assembly listing? # FIXME: this horribleness is ripped off from https://github.com/udem-dlteam/ribbit/blob/dev/src/host/py/rvm.py directly # What part of this happens at runtime? @@ -262,7 +305,7 @@ def push(x): if pos >= len(input)-1: break # TEMP x = get_code() - asm.comment(f"x = get_code() = {x}") + # asm.comment(f"x = get_code() = {x}") n = x d = 0 op = 0 @@ -272,50 +315,70 @@ def push(x): if n <= 2+d: break n -= d+3; op += 1 - asm.comment(f"op: {op}; n: {n}") + # asm.comment(f"op: {op}; n: {n}") if x > 90: n = pop() - asm.comment(f"[if] x: {x}; n = {n}") + # asm.comment(f"[if] x: {x}; n = {n}") else: if op == 0: - push(0) - stack = [0, stack] - asm.comment(f"op == 0; TODO: stack = cons(0, stack)") + push("#0") + # stack = [0, stack] + # asm.comment(f"op == 0; TODO: stack = cons(0, stack)") op += 1 if n == d: - n = get_int(0) - asm.comment(f"n = get_int(0) = {n}") + n = f"#{get_int(0)}" + # print(f"n = get_int(0) = {n}") + # asm.comment(f"n = get_int(0) = {n}") elif n >= d: #n = symbol_ref(get_int(n-d-1)) idx = get_int(n-d-1) - asm.comment(f"idx = get_int({n-d-1}) = {idx}") - n = f"symbol_ref({idx})" - asm.comment(f"{n} >= {d}; TODO: n = symbol_ref({idx})") + # asm.comment(f"idx = get_int({n-d-1}) = {idx}") + n = symbol_ref(idx) + # asm.comment(f"{n} >= {d}; TODO: n = symbol_ref({idx})") elif op < 3: - # n = symbol_ref(n) - n = f"symbol_ref({n})" - asm.comment(f"{op} < 3; TODO: n = {n}") + n = symbol_ref(n) + # asm.comment(f"{op} < 3; TODO: n = {n}") if op > 4: - # n = rib(rib(n, 0, pop()), nil, 1) # a proc, obviously - m = pop() - n = [[n, 0, m], "@rib_nil", 1] - asm.comment(f"proc: {n}, ...") - if not stack: break - op = 4 - - # FIXME: this is making a rib... - asm.comment(f"TOS = rib({op-1}, {n}, {stack[0]})") - # stack = ([op-1, n, stack[0]], stack[1]) - push([op-1, n, pop()]) - - # FIXME: initialize PC - # This definitely needs to happen at runtime, not here - asm.comment(f"TODO: pc = n[0][2] = {n[0][2]}") - - + # This is either a lambda, or the outer proc that wraps the whole program. + body = pop() + if not stack: + n = body + break + else: + params_lbl = asm.next_label("params") + emit_rib(params_lbl, n, "#0", body) + # FIXME: is this even close? + proc_label = asm.next_label("proc") + asm.label(proc_label) + asm.instr(f"@{params_lbl}") + asm.instr("@rib_nil") + asm.instr("#1") + # n = [[n, 0, m], "@rib_nil", 1] + # asm.comment(f"proc: {n}, ...") + n = f"@{proc_label}" + op = 4 + + # HACK: this seems to happen with integer constants and slot numbers. + # Make it happen in the right place? + if isinstance(n, int): + n = f"#{n}" + + instr_lbl = emit_instr(op-1, n, pop()) + push(f"@{instr_lbl}") + + # This will be the body of the outer proc, so just "jump" straight to it: + start_instr = n + # Note: emit_instr would want to choose the label... + emit_rib("main", "#0", start_instr, "#0", comment=f"jump {start_instr}") + + +FIRST_RIB = big.HEAP_BASE +"""Start of runtime-allocated ribs, which is the first word of RAM after the space which is mapped +to ROM. +""" def interpreter(asm): """ROM program implementing the RVM runtime, which interprets a program stored as "ribs" in ROM and RAM.""" @@ -334,8 +397,6 @@ def interpreter(asm): FIRST_PRIMITIVE = 8 LAST_PRIMITIVE = FIRST_PRIMITIVE + num_primitives - FIRST_RIB = big.HEAP_BASE - FALSE = "rib_false" TRUE = "rib_true" NIL = "rib_nil" @@ -357,12 +418,12 @@ def interpreter(asm): # asm.instr("A=M") # asm.instr("0;JMP") - def push_d(): - """Push the value in D onto the stack, without over-writing it.""" - asm.instr(f"@{SP}") - asm.instr("M=M-1") - asm.instr("A=M+1") # i.e., point to the previous location - asm.instr("M=D") + # def push_d(): + # """Push the value in D onto the stack, without over-writing it.""" + # asm.instr(f"@{SP}") + # asm.instr("M=M-1") + # asm.instr("A=M+1") # i.e., point to the previous location + # asm.instr("M=D") # def rib(x, y, z): # asm.comment(f"rib: {x}, {y}, {z}") @@ -501,9 +562,9 @@ def rib_append(val="D"): # asm.instr(f"@{SP}") # asm.instr("M=D") - asm.comment("PC = ?") - asm.comment("TODO") - # asm.instr("D=?") + asm.comment("PC = @main") + asm.instr("@main") + asm.instr("D=A") asm.instr(f"@{PC}") asm.instr("M=D") From db32534c944966bee23e22e859d60a8711877c8f Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 28 Jan 2024 15:17:12 -0500 Subject: [PATCH 031/221] Sketch out instruction loop --- alt/big.py | 8 +- alt/scheme/rvm.py | 288 +++++++++++++++++++++++++++++++++------------- 2 files changed, 216 insertions(+), 80 deletions(-) diff --git a/alt/big.py b/alt/big.py index 48e1eb7..82d11b3 100755 --- a/alt/big.py +++ b/alt/big.py @@ -221,7 +221,7 @@ def BigComputer(inputs, outputs): "SCREEN": SCREEN_BASE, "KEYBOARD": KEYBOARD_ADDR, "ROM": ROM_BASE, - "HEAP": HEAP_BASE, + "HEAP_MINUS_ONE": HEAP_BASE-1, # doesn't fit in 15 bits, so this points to the address before }, **solved_06.register_names(16) } @@ -240,14 +240,14 @@ def parse_op(string, symbols={}): return solved_06.parse_op(string, symbols) -def assemble(f, min_static=16, max_static=1023): +def assemble(f, min_static=16, max_static=1023, builtins=BUILTIN_SYMBOLS): """Standard assembler, except: shift the ROM base address, symbols for other base addresses, and 16-bit data.""" return solved_06.assemble(f, parse_op=parse_op, min_static=min_static, max_static=max_static, start_addr=ROM_BASE, - builtins=BUILTIN_SYMBOLS) + builtins=builtins) # @@ -287,6 +287,8 @@ def run(program, chip=BigComputer, name="Flat!", font="monaco-9", halt_addr=None if not halted and computer.pc == halt_addr: halted = True print(f"halted after {cycles} cycles") + trace(computer, cycles) + if not halted: # print(f"@{computer.pc}; outputs: {computer.outputs()}") diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 3a78f1a..2af2df4 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -24,7 +24,7 @@ def run(program): # TODO: custom BUILTIN_SYMBOLS # min_static = # max_static = - instrs, symbols, statics = big.assemble(asm.lines) + instrs, symbols, statics = big.assemble(asm.lines, min_static=None, builtins=BUILTINS) @@ -71,8 +71,8 @@ def trace(computer, cycles): print(f"{cycles:3,d}:") # TODO: decode these ribs/values - print(f" PC: @{computer.peek(0)}") - print(f" SP: @{computer.peek(1)}, ...") + print(f" SP: @{computer.peek(0)}, ...") + print(f" PC: @{computer.peek(1)}") next_rib = unsigned(computer.peek(2)) current_ribs = (next_rib - big.HEAP_BASE)//3 @@ -265,7 +265,7 @@ def symbol_ref(idx): """ # TODO: fix the inevitable of-by-one error(s) here asm.comment(f'symbol_ref({idx}); "{sym_names[idx][1]}"') - return f"#{FIRST_RIB + 6*(len(sym_names) - idx)}" + return f"#{big.HEAP_BASE + 6*(len(sym_names) - idx)}" def emit_instr(op, arg, next): lbl = asm.next_label("instr") @@ -375,27 +375,42 @@ def emit_instr(op, arg, next): emit_rib("main", "#0", start_instr, "#0", comment=f"jump {start_instr}") -FIRST_RIB = big.HEAP_BASE -"""Start of runtime-allocated ribs, which is the first word of RAM after the space which is mapped -to ROM. -""" +BUILTINS = { + **big.BUILTIN_SYMBOLS, + + # Low-memory "registers": + "SP": 0, + "PC": 1, + "NEXT_RIB": 2, + + "SYMBOL_TABLE": 4, # Only used during intiialization? + + # General-purpose temporary storage: + "TEMP_0": 5, + "TEMP_1": 6, + + # Useful values/addresses: + "FIRST_RIB_MINUS_ONE": big.HEAP_BASE-1, + "MAX_RIB": big.HEAP_TOP, +} + def interpreter(asm): """ROM program implementing the RVM runtime, which interprets a program stored as "ribs" in ROM and RAM.""" # Interpreter variables in low memory: - PC = 0 - SP = 1 - NEXT_RIB = 2 + # SP = 0 + # PC = 1 + # NEXT_RIB = 2 # RETURN = 3 # used only during initialization? - SYMBOL_TABLE = 4 + # SYMBOL_TABLE = 4 num_primitives = 22 - FIRST_PRIMITIVE = 8 - LAST_PRIMITIVE = FIRST_PRIMITIVE + num_primitives + # FIRST_PRIMITIVE = 8 + # LAST_PRIMITIVE = FIRST_PRIMITIVE + num_primitives FALSE = "rib_false" TRUE = "rib_true" @@ -441,63 +456,45 @@ def rib_append(val="D"): The value is either "D" (the default), or a value the ALU can produce (0 or 1, basically). """ - asm.instr(f"@{NEXT_RIB}") + asm.instr("@NEXT_RIB") asm.instr("M=M+1") asm.instr("A=M-1") asm.instr(f"M={val}") # TODO: find end of encoded program; initialize stack pointer below it - decode_stack_start = 16384//2 - 1 - asm.comment("SP = below the encoded program") - asm.instr(f"@{decode_stack_start}") - asm.instr("D=A") - asm.instr(f"@{SP}") - asm.instr("M=D") - asm.comment(f"NEXT_RIB = {FIRST_RIB} (HEAP_BASE)") - asm.instr(f"@{FIRST_RIB-1}") - asm.instr("D=A+1") - asm.instr(f"@{NEXT_RIB}") - asm.instr("M=D") - - # prim_rib_label = asm.next_label("prim_rib") - - # asm.comment("Initialize primitive vectors") - # for i, l in enumerate([prim_rib_label]): - # asm.instr(f"@{l}") - # asm.instr("D=A") - # asm.instr(f"@{FIRST_PRIMITIVE + i}") - # asm.instr("M=D") + # decode_stack_start = 16384//2 - 1 + # asm.comment("SP = below the encoded program") + # asm.instr(f"@{decode_stack_start}") + # asm.instr("D=A") + # asm.instr(f"@{SP}") + # asm.instr("M=D") - # asm.comment("Initialize shared constants") - # for n in ("false", "true", "nil"): - # asm.comment(f"{n} = rib(0, 0, 5)") - # asm.instr("D=0") - # push_d() - # push_d() - # asm.instr("@5") - # asm.instr("D=A") - # push_d() - # call_primitive(prim_rib_label) + asm.comment("NEXT_RIB = FIRST_RIB (HEAP_BASE)") + asm.instr("@FIRST_RIB_MINUS_ONE") # Note: this value is one lower than the actual start of the heap, to fit in 15 bits + asm.instr("D=A+1") # Tricky: add 1 to bring the address up to 0x8000 + asm.instr("@NEXT_RIB") + asm.instr("M=D") + asm.blank() asm.comment("Construct the symbol table in RAM:") asm.comment("SYMBOL_TABLE = '()") asm.instr("@rib_nil") asm.instr("D=A") - asm.instr(f"@{SYMBOL_TABLE}") + asm.instr("@SYMBOL_TABLE") asm.instr("M=D") - asm.comment("R5 = ptr") - asm.instr(f"@symbol_names_start") + asm.comment("R5 = table start") + asm.instr("@symbol_names_start") asm.instr("D=A") - asm.instr("@R5") + asm.instr("@TEMP_0") asm.instr("M=D") - loop = "symbol_table_loop" - asm.label(loop) + symbol_table_loop = "symbol_table_loop" + asm.label(symbol_table_loop) asm.comment("DEBUG: log the pointer to tty") - asm.instr("@R5") + asm.instr("@TEMP_0") asm.instr("D=M") asm.instr("@KEYBOARD") asm.instr("M=D") @@ -506,79 +503,110 @@ def rib_append(val="D"): asm.instr("@rib_false") asm.instr("D=A") rib_append() - asm.instr("@R5") + asm.instr("@TEMP_0") asm.instr("D=M") rib_append() asm.instr("@2") # symbol asm.instr("D=A") rib_append() + asm.blank() + # TODO: these `pair` ribs are actually constant and could live in the ROM, with pre-computed + # addresses pointing to the `symbol` ribs being allocated just above. + # Actually, is this list even used by the library (`string->symbol`)? asm.comment("SYMBOL_TABLE = new pair") asm.comment("car = the rib we just wrote") - asm.instr(f"@{NEXT_RIB}") + asm.instr("@NEXT_RIB") asm.instr("D=M") asm.instr("@3") asm.instr("D=D-A") rib_append() asm.comment("cdr = (old) SYMBOL_TABLE") - asm.instr(f"@{SYMBOL_TABLE}") + asm.instr("@SYMBOL_TABLE") asm.instr("D=M") rib_append() rib_append("0") # pair - asm.instr(f"@{NEXT_RIB}") + asm.blank() + + asm.comment("update SYMBOL_TABLE") + asm.instr("@NEXT_RIB") asm.instr("D=M") asm.instr("@3") asm.instr("D=D-A") - asm.instr(f"@{SYMBOL_TABLE}") + asm.instr("@SYMBOL_TABLE") asm.instr("M=D") asm.comment("DEBUG: log NEXT_RIB to tty") - asm.instr(f"@{NEXT_RIB}") + asm.instr("@NEXT_RIB") asm.instr("D=M") asm.instr("@KEYBOARD") asm.instr("M=D") asm.comment("increment R5") - asm.instr("@R5") + asm.instr("@TEMP_0") asm.instr("M=M+1") asm.comment("D = compare(R5, symbol_names_end)") asm.instr("D=M") asm.instr("@symbol_names_end") - asm.instr("D=D-M") - asm.instr(f"@{loop}") + asm.instr("D=D-A") + asm.instr(f"@{symbol_table_loop}") asm.instr("D;JLT") + asm.blank() - # decode_label = "decode_loop" - # asm.label(decode_label) - # asm.instr(f"@{decode_label}") - # asm.instr("0;JMP") - # # Now move the stack pointer to the top of memory (over-writing the encoded program) - # asm.comment("SP = top of memory") - # asm.instr("@16384") - # asm.instr("D=A") - # asm.instr(f"@{SP}") - # asm.instr("M=D") + # + # Initialize interpreter state: + # + + asm.comment("SP = '()") + asm.instr("@rib_nil") + asm.instr("D=A") + asm.instr("@SP") + asm.instr("M=D") asm.comment("PC = @main") asm.instr("@main") asm.instr("D=A") - asm.instr(f"@{PC}") + asm.instr("@PC") asm.instr("M=D") + asm.blank() + # # Exec loop: # - exec_label = "exec_loop" - asm.label(exec_label) + asm.comment("First time: jump to the main loop") + asm.instr("@exec_loop") + asm.instr("0;JMP") + + asm.comment("Typical loop path: get the next instruction to interpret from the third field of the current instruction:") + asm.label("continue_next") + asm.instr("@PC") + asm.instr("A=M+1") + asm.instr("A=A+1") # Cheeky: add two ones instead of using @2 to save a cycle + asm.instr("D=M") + asm.instr("@PC") + asm.instr("M=D") + + asm.comment("sanity check: zero is never the address of a valid instr") + asm.instr("@halt_loop") + asm.instr("D;JEQ") + asm.comment("...fallthrough") + asm.blank() + + asm.label("exec_loop") asm.comment("TODO") asm.instr("D=D") # asm.instr(f"@{exec_label}") # asm.instr("0;JMP") + asm.comment("TEMP: just go to 'next' no matter what") + asm.instr("@continue_next") + asm.instr("0;JMP") + # # Halt: @@ -589,10 +617,116 @@ def rib_append(val="D"): asm.instr(f"@{halt_label}") asm.instr("0;JMP") - # asm.label("prim_rib_label") - # asm.comment("TODO:") + + # + # Primitive handlers: + # + + asm.label("primitive_rib") + asm.comment("primitive 0; rib :: x y z -- rib(x, y, z)") + asm.comment("TODO:") # asm.comment("- check for stack-heap collision") # asm.comment("- allocate space for new rib") # asm.comment("- pop three values and copy them into the new rib") - # return_from_primitive() + asm.instr("@exec_loop") + asm.instr("0;JMP") + asm.blank() + + asm.comment("TODO: for now, all these just fall through to halt/unimp") + asm.label("primitive_id") + asm.comment("primitive 1; id :: x -- x") + asm.label("primitive_arg1") + asm.comment("primitive 2; arg1 :: x y -- x") + asm.label("primitive_arg2") + asm.comment("primitive 3; arg2 :: x y -- y") + asm.label("primitive_close") + asm.comment("primitive 4; close :: x -- rib(x[0], stack, 1)") + asm.label("primitive_rib?") + asm.comment("primitive 5; rib? :: x -- bool(x is a rib)") + asm.label("primitive_field0") + asm.comment("primitive 6; field0 :: rib(x, _, _) -- x") + asm.label("primitive_field1") + asm.comment("primitive 7; field1 :: rib(_, y, _) -- y") + asm.label("primitive_field2") + asm.comment("primitive 8; field2 :: rib(_, _, z) -- z") + asm.label("primitive_field0-set!") + asm.comment("primitive 9; field0-set! :: rib(_, y, z) x -- x (and update the rib in place: rib(x, y, z))") + asm.label("primitive_field1-set!") + asm.comment("primitive 10; field1-set! :: rib(x, _, z) y -- y (and update the rib in place: rib(x, y, z))") + asm.label("primitive_field2-set!") + asm.comment("primitive 11; field2-set! :: rib(x, y, _) z -- z (and update the rib in place: rib(x, y, z))") + asm.label("primitive_eqv?") + asm.comment("primitive 12; eqv? :: x y -- bool(x is identical to y)") + asm.label("primitive_<") + asm.comment("primitive 13; < :: x y -- bool(x < y)") + asm.label("primitive_+") + asm.comment("primitive 14; + :: x y -- x + y") + asm.label("primitive_-") + asm.comment("primitive 15; - :: x y -- x - y") + asm.label("primitive_*") + asm.comment("primitive 16; - :: x y -- x * y") + asm.label("primitive_peek") + asm.comment("primitive 19; peek :: x -- RAM[x]") + asm.label("primitive_poke") + asm.comment("primitive 20; poke :: x y -- y (and write the valu y at RAM[x])") + + asm.label("primitive_halt") + asm.comment("primitive 21; halt :: -- (no more instructions are executed)") + asm.instr("@halt_loop") + asm.instr("0;JMP") + + asm.label("primitive_unimp") + asm.comment("Note: the current instr will be logged if tracing is enabled") + asm.instr("@halt_loop") + asm.instr("0;JMP") + asm.comment("=== Primitive vectors ===") + asm.label("primitive_vector_table_start") + asm.comment("0") + asm.instr("@primitive_rib") + asm.comment("1") + asm.instr("@primitive_id") + asm.comment("2") + asm.instr("@primitive_arg1") + asm.comment("3") + asm.instr("@primitive_arg2") + asm.comment("4") + asm.instr("@primitive_close") + asm.comment("5") + asm.instr("@primitive_rib?") + asm.comment("6") + asm.instr("@primitive_field0") + asm.comment("7") + asm.instr("@primitive_field1") + asm.comment("8") + asm.instr("@primitive_field2") + asm.comment("9") + asm.instr("@primitive_field0-set!") + asm.comment("10") + asm.instr("@primitive_field1-set!") + asm.comment("11") + asm.instr("@primitive_field2-set!") + asm.comment("12") + asm.instr("@primitive_eqv?") + asm.comment("13") + asm.instr("@primitive_<") + asm.comment("14") + asm.instr("@primitive_+") + asm.comment("15") + asm.instr("@primitive_-") + asm.comment("16") + asm.instr("@primitive_*") + asm.comment("17 (quotient: not implemented)") + asm.instr("@primitive_unimp") + asm.comment("18 (getchar: not implemented)") + asm.instr("@primitive_unimp") + asm.comment("19 (putchar: not implemented)") + asm.instr("@primitive_unimp") + asm.comment("20") + asm.instr("@primitive_peek") + asm.comment("21") + asm.instr("@primitive_poke") + asm.comment("22") + asm.instr("@primitive_halt") + # fatal? + asm.label("primitive_vectors_end") From 9e055cb054c54795ca1f14625ad2d6696b298c5c Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Mon, 29 Jan 2024 21:07:25 -0500 Subject: [PATCH 032/221] Better tracing --- alt/scheme/rvm.py | 40 ++++++++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 2af2df4..88b8a54 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -67,14 +67,43 @@ def run(program): # } def trace(computer, cycles): + def peek(addr): + """Read from RAM or ROM.""" + if big.ROM_BASE <= addr < big.HEAP_BASE: + return computer._rom.storage[addr] + else: + return computer.peek(addr) + + def show_instr(addr): + x, y, z = peek(addr), peek(addr+1), peek(addr+2) + if x == 0 and z == 0: + return f"jump {y}" + elif x == 0: + return f"call {y} -> {z}" + elif x == 1: + return f"set {y} -> {z}" + elif x == 2: + return f"get {y} -> {z}" + elif x == 3: + return f"const {y} -> {z}" + elif x == 4: + return f"if -> {y} else {z}" + else: + return f"not an instr: {(x, y, z)}" + + def show_stack(addr): + if addr == symbols["rib_nil"]: + return "'()" + elif peek(addr+2) == 0: # pair + return f"{peek(addr)} : {show_stack(peek(addr+1))}" + if computer.fetch and computer.pc == symbols["exec_loop"]: print(f"{cycles:3,d}:") - # TODO: decode these ribs/values - print(f" SP: @{computer.peek(0)}, ...") - print(f" PC: @{computer.peek(1)}") + print(f" SP: @{peek(0)}, {show_stack(peek(0))}") + print(f" PC: @{peek(1)}; {show_instr(peek(1))}") - next_rib = unsigned(computer.peek(2)) + next_rib = unsigned(peek(2)) current_ribs = (next_rib - big.HEAP_BASE)//3 max_ribs = (big.HEAP_TOP - big.HEAP_BASE)//3 print(f" heap: {current_ribs:3,d} ({100*current_ribs/max_ribs:0.1f}%)") @@ -178,8 +207,7 @@ def get_int(n): def emit_rib(lbl, x, y, z, comment=None): - if lbl: - asm.label(lbl) + asm.label(lbl) if comment: asm.comment(comment) asm.instr(x) From b211a7d0bba469b6ee317a89221608401363daa0 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Mon, 29 Jan 2024 21:53:44 -0500 Subject: [PATCH 033/221] Handle first instruction type: jump to global --- alt/scheme/rvm.py | 91 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 81 insertions(+), 10 deletions(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 88b8a54..4546813 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -1,6 +1,8 @@ #! /usr/bin/env python """Ribbit VM implementation. + +See http://www.iro.umontreal.ca/~feeley/papers/YvonFeeleyVMIL21.pdf for the general picture. """ import computer @@ -107,7 +109,10 @@ def show_stack(addr): current_ribs = (next_rib - big.HEAP_BASE)//3 max_ribs = (big.HEAP_TOP - big.HEAP_BASE)//3 print(f" heap: {current_ribs:3,d} ({100*current_ribs/max_ribs:0.1f}%)") - + elif computer.fetch and computer.pc in symbols: + print(f"{cycles:3,d}: ({symbols[computer.pc]})") + elif computer.fetch: + print(f"{cycles:3,d}: {computer.pc}") big.run(program=instrs, name="Scheme", @@ -171,6 +176,8 @@ def special(name): special("rib_true") special("rib_nil") + asm.blank() + # asm.label("input_start") # # TODO: decode from characters into raw byte/int values @@ -232,6 +239,8 @@ def emit_vector(lbl, elems, count: int): emit_rib(lbl, elems, f"#{count}", "#4" n = get_int(0) sym_names = n*[("rib_string_empty", "")] + asm.blank() + accum = "@rib_nil" acc_str = "" idx = 0 @@ -270,6 +279,7 @@ def emit_vector(lbl, elems, count: int): emit_rib(lbl, elems, f"#{count}", "#4" asm.instr(f"@{lbl}") asm.label("symbol_names_end") + asm.blank() # Decode RVM instructions: @@ -424,7 +434,10 @@ def emit_instr(op, arg, next): def interpreter(asm): - """ROM program implementing the RVM runtime, which interprets a program stored as "ribs" in ROM and RAM.""" + """ROM program implementing the RVM runtime, which interprets a program stored as "ribs" in ROM and RAM. + + This part of the ROM is the same, independent of the Scheme program that's being interpreted. + """ # Interpreter variables in low memory: # SP = 0 @@ -440,6 +453,9 @@ def interpreter(asm): # FIRST_PRIMITIVE = 8 # LAST_PRIMITIVE = FIRST_PRIMITIVE + num_primitives + # The largest value that can ever refer to a slot, as opposed to a + MAX_SLOT = big.ROM_BASE-1 + FALSE = "rib_false" TRUE = "rib_true" NIL = "rib_nil" @@ -606,16 +622,33 @@ def rib_append(val="D"): # Exec loop: # + def pc_x_to_d(): + """Load the first field of the current instruction to D.""" + asm.instr("@PC") + asm.instr("A=M") + asm.instr("D=M") + + def pc_y_to_d(): + """Load the middle field of the current instruction to D.""" + asm.instr("@PC") + asm.instr("A=M+1") + asm.instr("D=M") + + def pc_z_to_d(): + """Load the last field of the current instruction to D.""" + # Note: could save an instruction here if the pointer pointed to the *middle* field. + asm.instr("@PC") + asm.instr("A=M+1") + asm.instr("A=A+1") # Cheeky: add two ones instead of using @2 to save a cycle + asm.instr("D=M") + asm.comment("First time: jump to the main loop") asm.instr("@exec_loop") asm.instr("0;JMP") asm.comment("Typical loop path: get the next instruction to interpret from the third field of the current instruction:") asm.label("continue_next") - asm.instr("@PC") - asm.instr("A=M+1") - asm.instr("A=A+1") # Cheeky: add two ones instead of using @2 to save a cycle - asm.instr("D=M") + pc_z_to_d() asm.instr("@PC") asm.instr("M=D") @@ -626,10 +659,46 @@ def rib_append(val="D"): asm.blank() asm.label("exec_loop") + + asm.comment("Sanity check: negative instruction type is impossible") + pc_x_to_d() + asm.instr("@halt_loop") + asm.instr("D;JLT") + + asm.instr("@code_gt_zero") + asm.instr("D;JGT") + + asm.comment("type 0: jump/call") + pc_z_to_d() + asm.instr("@handle_call") + asm.instr("D;JNE") + pc_y_to_d() + asm.instr(f"@{MAX_SLOT}") + asm.instr("D=D-A") + asm.instr("@handle_jump_to_slot") + asm.instr("D;JLT") + pc_y_to_d() + asm.instr("@PC") + asm.instr("M=D") + asm.instr("@exec_loop") + asm.instr("0;JMP") + + asm.label("handle_jump_to_slot") asm.comment("TODO") - asm.instr("D=D") - # asm.instr(f"@{exec_label}") - # asm.instr("0;JMP") + asm.instr("@halt_loop") + asm.instr("0;JMP") + + + asm.label("handle_call") + asm.comment("TODO") + asm.instr("@halt_loop") + asm.instr("0;JMP") + + + asm.label("code_gt_zero") + asm.comment("TODO") + asm.instr("@halt_loop") + asm.instr("0;JMP") asm.comment("TEMP: just go to 'next' no matter what") asm.instr("@continue_next") @@ -696,7 +765,7 @@ def rib_append(val="D"): asm.label("primitive_peek") asm.comment("primitive 19; peek :: x -- RAM[x]") asm.label("primitive_poke") - asm.comment("primitive 20; poke :: x y -- y (and write the valu y at RAM[x])") + asm.comment("primitive 20; poke :: x y -- y (and write the value y at RAM[x])") asm.label("primitive_halt") asm.comment("primitive 21; halt :: -- (no more instructions are executed)") @@ -758,3 +827,5 @@ def rib_append(val="D"): asm.instr("@primitive_halt") # fatal? asm.label("primitive_vectors_end") + + asm.blank() \ No newline at end of file From 8c6ba260b3df6ed16ca521679e9d35975d6b3613 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Tue, 30 Jan 2024 11:21:16 -0500 Subject: [PATCH 034/221] More tracing options --- alt/big.py | 23 ++++++++------- alt/scheme/rvm.py | 75 ++++++++++++++++++++++++++++------------------- 2 files changed, 57 insertions(+), 41 deletions(-) diff --git a/alt/big.py b/alt/big.py index 82d11b3..aa39bc8 100755 --- a/alt/big.py +++ b/alt/big.py @@ -259,7 +259,7 @@ def assemble(f, min_static=16, max_static=1023, builtins=BUILTIN_SYMBOLS): import time -def run(program, chip=BigComputer, name="Flat!", font="monaco-9", halt_addr=None, trace=None): +def run(program, chip=BigComputer, name="Flat!", font="monaco-9", halt_addr=None, trace=None, verbose_tty=True): """Run with keyboard and text-mode graphics.""" # TODO: font @@ -291,20 +291,19 @@ def run(program, chip=BigComputer, name="Flat!", font="monaco-9", halt_addr=None if not halted: - # print(f"@{computer.pc}; outputs: {computer.outputs()}") - # print(f" R0: {computer.peek(0)}; R1: {computer.peek(1)}") - - # computer.ticktock(100) - # cycles += 100 - computer.ticktock() - cycles += 1 + if trace is not None: + cycles_per_call = 1 + else: + cycles_per_call = 10 # has to be a factor of CYCLES_PER_UPDATE + computer.ticktock(cycles_per_call) + cycles += cycles_per_call else: time.sleep(0.1) if trace is not None: trace(computer, cycles) - CYCLES_PER_UPDATE = 5 + CYCLES_PER_UPDATE = 2 # HACK: to ensure we see all the TTY output else: CYCLES_PER_UPDATE = 100 @@ -314,9 +313,11 @@ def run(program, chip=BigComputer, name="Flat!", font="monaco-9", halt_addr=None tty_char = computer.get_tty() if tty_char: - # print(chr(tty_char), end="", flush=True) if 32 < tty_char <= 127: - print(f"TTY: {repr(chr(tty_char))} ({tty_char})") + if not verbose_tty: + print(chr(tty_char), end="", flush=True) + else: + print(f"TTY: {repr(chr(tty_char))} ({tty_char})") else: print(f"TTY: ({tty_char:6d}; 0x{unsigned(tty_char):04x})") diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 4546813..e1bef2c 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -11,7 +11,18 @@ from alt import big from nand.vector import unsigned -def run(program): + +TRACE_NONE = 0 +"""No logging of Ribbit instructions.""" +TRACE_COARSE = 1 +"""Log each Ribbit instruction before it's interpreted, with a summary of the stack and heap.""" +TRACE_FINE = 2 +"""Log at each branch point of the interpreter loop (in addition to the COARSE logging.)""" +TRACE_ALL = 3 +"""Log every CPU instruction (in addition to COARSE and FINE logging.)""" + + +def run(program, print_asm=True, trace_level=TRACE_COARSE, verbose_tty=True): encoded = compile(program) print(f"encoded program: {repr(encoded)}") @@ -29,22 +40,7 @@ def run(program): instrs, symbols, statics = big.assemble(asm.lines, min_static=None, builtins=BUILTINS) - -# Example: "(poke 16384 21845)" -# proc(nil): -# call(#0) -# const(#21) -# const(#0) -# const(#1) -# call(symbol(#f, 'rib')@4378049088) -# set(symbol(#f, 'poke')@4378050432) -# const(#16384) -# const(#21845) -# jump(symbol(#f, 'poke')@4378050432) - - - PRINT = True - if PRINT: + if print_asm: for l in asm.lines: print(l) print() @@ -58,17 +54,31 @@ def run(program): # print(f"Encoded program: {program_words} ({100*program_words/available_ram:0.2f}%)") print() - for name, addr in sorted(symbols.items(), key=lambda t: t[1]): - print(f" {name}: {addr}") - # # This is enough to get minimal tracing from the standard driver in computer.py. - # # It will just log the cycle count each time we go through each of the main loops. - # src_map = { - # # symbols["decode_loop"]: "decode", - # symbols["exec_loop"]: "exec" - # } + def show_map(label, m): + print("\n".join( + [ f"{label}:" ] + + [ f" {addr:5d}: {name}" + for name, addr in sorted(m.items(), key=lambda t: t[1]) + ] + + [""] + )) + + show_map("Symbols", symbols) + + if statics != {}: + show_map("Statics", statics) + + + last_traced_exec = None def trace(computer, cycles): + nonlocal last_traced_exec + + # Only log on one of the half-cycles: + if not computer.fetch: + return + def peek(addr): """Read from RAM or ROM.""" if big.ROM_BASE <= addr < big.HEAP_BASE: @@ -99,8 +109,12 @@ def show_stack(addr): elif peek(addr+2) == 0: # pair return f"{peek(addr)} : {show_stack(peek(addr+1))}" - if computer.fetch and computer.pc == symbols["exec_loop"]: - print(f"{cycles:3,d}:") + if trace_level >= TRACE_COARSE and computer.pc == symbols["exec_loop"]: + if last_traced_exec is None: + print(f"{cycles:,d}:") + else: + print(f"{cycles:,d} (+{cycles - last_traced_exec:,d}):") + last_traced_exec = cycles print(f" SP: @{peek(0)}, {show_stack(peek(0))}") print(f" PC: @{peek(1)}; {show_instr(peek(1))}") @@ -109,15 +123,16 @@ def show_stack(addr): current_ribs = (next_rib - big.HEAP_BASE)//3 max_ribs = (big.HEAP_TOP - big.HEAP_BASE)//3 print(f" heap: {current_ribs:3,d} ({100*current_ribs/max_ribs:0.1f}%)") - elif computer.fetch and computer.pc in symbols: + elif trace_level >= TRACE_FINE and computer.pc in symbols: print(f"{cycles:3,d}: ({symbols[computer.pc]})") - elif computer.fetch: + elif trace_level >= TRACE_ALL: print(f"{cycles:3,d}: {computer.pc}") big.run(program=instrs, name="Scheme", halt_addr=symbols["halt_loop"], - trace=trace) + trace=trace if trace_level > TRACE_NONE else None, + verbose_tty=verbose_tty) def compile(src): From 7a985ba4840037c2f3b310532554fad3d3ceccc4 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Thu, 1 Feb 2024 18:12:18 -0500 Subject: [PATCH 035/221] More tracing tweaks --- alt/scheme/rvm.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index e1bef2c..e4bdf48 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -117,12 +117,13 @@ def show_stack(addr): last_traced_exec = cycles print(f" SP: @{peek(0)}, {show_stack(peek(0))}") - print(f" PC: @{peek(1)}; {show_instr(peek(1))}") next_rib = unsigned(peek(2)) current_ribs = (next_rib - big.HEAP_BASE)//3 max_ribs = (big.HEAP_TOP - big.HEAP_BASE)//3 print(f" heap: {current_ribs:3,d} ({100*current_ribs/max_ribs:0.1f}%)") + print(f" PC: @{peek(1)}") + print(f" {show_instr(peek(1))}") elif trace_level >= TRACE_FINE and computer.pc in symbols: print(f"{cycles:3,d}: ({symbols[computer.pc]})") elif trace_level >= TRACE_ALL: From ed789cb40e91ae44fcb9f77c2530efe135c2202f Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Thu, 1 Feb 2024 18:37:01 -0500 Subject: [PATCH 036/221] Fix TRACE_FINE so it works; cleanup --- alt/scheme/rvm.py | 34 ++++++++-------------------------- 1 file changed, 8 insertions(+), 26 deletions(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index e4bdf48..71056b9 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -25,7 +25,7 @@ def run(program, print_asm=True, trace_level=TRACE_COARSE, verbose_tty=True): encoded = compile(program) - print(f"encoded program: {repr(encoded)}") + # print(f"encoded program: {repr(encoded)}") asm = AssemblySource() @@ -33,12 +33,10 @@ def run(program, print_asm=True, trace_level=TRACE_COARSE, verbose_tty=True): decode(encoded, asm) - # TODO: how many statics? - # TODO: custom BUILTIN_SYMBOLS - # min_static = - # max_static = instrs, symbols, statics = big.assemble(asm.lines, min_static=None, builtins=BUILTINS) + assert symbols["start"] == big.ROM_BASE + if print_asm: for l in asm.lines: print(l) @@ -71,6 +69,7 @@ def show_map(label, m): last_traced_exec = None + symbols_by_addr = { addr: name for (name, addr) in symbols.items() } def trace(computer, cycles): nonlocal last_traced_exec @@ -124,8 +123,8 @@ def show_stack(addr): print(f" heap: {current_ribs:3,d} ({100*current_ribs/max_ribs:0.1f}%)") print(f" PC: @{peek(1)}") print(f" {show_instr(peek(1))}") - elif trace_level >= TRACE_FINE and computer.pc in symbols: - print(f"{cycles:3,d}: ({symbols[computer.pc]})") + elif trace_level >= TRACE_FINE and computer.pc in symbols_by_addr: + print(f"{cycles:3,d}: ({symbols_by_addr[computer.pc]})") elif trace_level >= TRACE_ALL: print(f"{cycles:3,d}: {computer.pc}") @@ -147,11 +146,6 @@ def compile(src): return result.stdout -# $2gra,ekop,,,,;'v[_LvXs+!'?X&lkv8i)!):ok:okw#!(:lkny -# -# symbol table: ","-separated, ";"-terminated: -# - def decode(input, asm): """Decode the compiler's output as data in ROM. @@ -194,18 +188,6 @@ def special(name): asm.blank() - # asm.label("input_start") - - # # TODO: decode from characters into raw byte/int values - # for c in input: - # asm.instr(f"#{hex(ord(c))}") - - # asm.label("input_end") - - # def emit_cons(x, y): return emit_rib(x, y, "#0") - # def emit_symbol(x) - - # for comprehension: pos = -1 def get_byte(): @@ -225,8 +207,6 @@ def get_int(n): return 46*n + x else: return get_int(46*n + x-46) - # n *= 46 - # return n+x if x < 46 else get_int(n + x - 46) def emit_rib(lbl, x, y, z, comment=None): @@ -284,6 +264,7 @@ def emit_vector(lbl, elems, count: int): emit_rib(lbl, elems, f"#{count}", "#4" accum = f"@{lbl}" acc_str = chr(c) + acc_str + asm.blank() # TODO: move this table elsewhere, so the ribs for strings and instructions form a monolithic # block of address space? @@ -530,6 +511,7 @@ def rib_append(val="D"): # asm.instr(f"@{SP}") # asm.instr("M=D") + asm.label("start") asm.comment("NEXT_RIB = FIRST_RIB (HEAP_BASE)") asm.instr("@FIRST_RIB_MINUS_ONE") # Note: this value is one lower than the actual start of the heap, to fit in 15 bits asm.instr("D=A+1") # Tricky: add 1 to bring the address up to 0x8000 From c1c3b631878bc865f1e76015109e015979ff1e5a Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Thu, 1 Feb 2024 18:38:11 -0500 Subject: [PATCH 037/221] Implement const with integer operand (no check) --- alt/scheme/rvm.py | 57 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 71056b9..20b4965 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -694,14 +694,69 @@ def pc_z_to_d(): asm.label("code_gt_zero") + asm.instr("D=D-1") + asm.instr("@code_gt_one") + asm.instr("D;JGT") + + asm.comment("type 1: set") asm.comment("TODO") asm.instr("@halt_loop") asm.instr("0;JMP") - asm.comment("TEMP: just go to 'next' no matter what") + asm.label("code_gt_one") + asm.instr("D=D-1") + asm.instr("@code_gt_two") + asm.instr("D;JGT") + + asm.comment("type 2: get") + asm.comment("TODO") + asm.instr("@halt_loop") + asm.instr("0;JMP") + + asm.label("code_gt_two") + asm.instr("D=D-1") + asm.instr("@code_gt_three") + asm.instr("D;JGT") + + asm.comment("type 3: const") + pc_y_to_d() + asm.comment("TODO: check tag") + asm.comment("Allocate rib: x = pc.y") + rib_append() + asm.comment("y = SP") + asm.instr("@SP") + asm.instr("D=M") + rib_append() + asm.comment("z = 0 (pair)") + rib_append("0") + + asm.comment("SP = just-allocated rib") + asm.instr("@NEXT_RIB") + asm.instr("D=M") + asm.instr("@3") + asm.instr("D=D-A") + asm.instr("@SP") + asm.instr("M=D") + asm.instr("@continue_next") asm.instr("0;JMP") + asm.label("code_gt_three") + asm.instr("D=D-1") + asm.instr("@halt_loop") + asm.instr("D;JGT") + + asm.comment("type 4: if") + asm.comment("TODO") + asm.instr("@halt_loop") + asm.instr("0;JMP") + + asm.blank() + + # asm.comment("TEMP: just go to 'next' no matter what") + # asm.instr("@continue_next") + # asm.instr("0;JMP") + # # Halt: From e750654c555be3a4f89fdb3fff29efcfaeaa71fc Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Thu, 1 Feb 2024 18:39:39 -0500 Subject: [PATCH 038/221] Remove dead comments --- alt/scheme/rvm.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 20b4965..1e7c9f3 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -340,7 +340,6 @@ def emit_instr(op, arg, next): if pos >= len(input)-1: break # TEMP x = get_code() - # asm.comment(f"x = get_code() = {x}") n = x d = 0 op = 0 @@ -350,31 +349,20 @@ def emit_instr(op, arg, next): if n <= 2+d: break n -= d+3; op += 1 - # asm.comment(f"op: {op}; n: {n}") - if x > 90: n = pop() - # asm.comment(f"[if] x: {x}; n = {n}") else: if op == 0: push("#0") - # stack = [0, stack] - # asm.comment(f"op == 0; TODO: stack = cons(0, stack)") op += 1 if n == d: n = f"#{get_int(0)}" - # print(f"n = get_int(0) = {n}") - # asm.comment(f"n = get_int(0) = {n}") elif n >= d: - #n = symbol_ref(get_int(n-d-1)) idx = get_int(n-d-1) - # asm.comment(f"idx = get_int({n-d-1}) = {idx}") n = symbol_ref(idx) - # asm.comment(f"{n} >= {d}; TODO: n = symbol_ref({idx})") elif op < 3: n = symbol_ref(n) - # asm.comment(f"{op} < 3; TODO: n = {n}") if op > 4: # This is either a lambda, or the outer proc that wraps the whole program. From 302b1d29977e94190d83cbc191d49ff8abd40597 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Fri, 2 Feb 2024 09:49:32 -0500 Subject: [PATCH 039/221] More tracing improvements --- alt/scheme/rvm.py | 54 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 1e7c9f3..bdde50a 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -86,27 +86,53 @@ def peek(addr): return computer.peek(addr) def show_instr(addr): + def show_target(val): + if val <= BUILTINS["MAX_SLOT"]: + return f"#{val}" + else: + return show_addr(val) + x, y, z = peek(addr), peek(addr+1), peek(addr+2) if x == 0 and z == 0: - return f"jump {y}" + return f"jump {show_target(y)}" elif x == 0: - return f"call {y} -> {z}" + return f"call {show_target(y)} -> {show_addr(z)}" elif x == 1: - return f"set {y} -> {z}" + return f"set {show_target(y)} -> {show_addr(z)}" elif x == 2: - return f"get {y} -> {z}" + return f"get {show_target(y)} -> {show_addr(z)}" elif x == 3: - return f"const {y} -> {z}" + return f"const {show_obj(y)} -> {show_addr(z)}" elif x == 4: - return f"if -> {y} else {z}" + return f"if -> {show_addr(y)} else {show_addr(z)}" else: return f"not an instr: {(x, y, z)}" + def show_obj(val, deep=True): + # FIXME: check tag + if val < big.ROM_BASE: + return f"{val}" + else: + x, y, z = peek(val), peek(val+1), peek(val+2) + if deep: + return f"({show_obj(x), show_obj(y), show_obj(z)}" + else: + return f"({x, y, z}" + def show_stack(addr): + def go(addr): if addr == symbols["rib_nil"]: - return "'()" + return [] elif peek(addr+2) == 0: # pair - return f"{peek(addr)} : {show_stack(peek(addr+1))}" + return go(peek(addr+1)) + [show_obj(peek(addr))] + return ", ".join(go(addr)) + + def show_addr(addr): + """Show an address (with "@"), using the symbol for addresses in ROM.""" + if addr in symbols_by_addr: + return f"@{symbols_by_addr[addr]}" + else: + return f"@{unsigned(addr)}" if trace_level >= TRACE_COARSE and computer.pc == symbols["exec_loop"]: if last_traced_exec is None: @@ -115,13 +141,13 @@ def show_stack(addr): print(f"{cycles:,d} (+{cycles - last_traced_exec:,d}):") last_traced_exec = cycles - print(f" SP: @{peek(0)}, {show_stack(peek(0))}") + print(f" stack: ({show_addr(peek(0))}) {show_stack(peek(0))}") next_rib = unsigned(peek(2)) current_ribs = (next_rib - big.HEAP_BASE)//3 max_ribs = (big.HEAP_TOP - big.HEAP_BASE)//3 print(f" heap: {current_ribs:3,d} ({100*current_ribs/max_ribs:0.1f}%)") - print(f" PC: @{peek(1)}") + print(f" PC: {show_addr(peek(1))}") print(f" {show_instr(peek(1))}") elif trace_level >= TRACE_FINE and computer.pc in symbols_by_addr: print(f"{cycles:3,d}: ({symbols_by_addr[computer.pc]})") @@ -415,6 +441,9 @@ def emit_instr(op, arg, next): # Useful values/addresses: "FIRST_RIB_MINUS_ONE": big.HEAP_BASE-1, "MAX_RIB": big.HEAP_TOP, + + # The largest value that can ever by the index of a slot, as opposed to the address of a global (symbol) + "MAX_SLOT": big.ROM_BASE-1, } @@ -438,9 +467,6 @@ def interpreter(asm): # FIRST_PRIMITIVE = 8 # LAST_PRIMITIVE = FIRST_PRIMITIVE + num_primitives - # The largest value that can ever refer to a slot, as opposed to a - MAX_SLOT = big.ROM_BASE-1 - FALSE = "rib_false" TRUE = "rib_true" NIL = "rib_nil" @@ -659,7 +685,7 @@ def pc_z_to_d(): asm.instr("@handle_call") asm.instr("D;JNE") pc_y_to_d() - asm.instr(f"@{MAX_SLOT}") + asm.instr(f"@MAX_SLOT") asm.instr("D=D-A") asm.instr("@handle_jump_to_slot") asm.instr("D;JLT") From f4d815a8a5d4ba031e8b76813cefca18115dc70c Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Fri, 2 Feb 2024 13:17:15 -0500 Subject: [PATCH 040/221] =?UTF-8?q?Don=E2=80=99t=20use=20bogus=20jump=20fo?= =?UTF-8?q?r=20main?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- alt/scheme/rvm.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index bdde50a..effffdd 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -421,7 +421,10 @@ def emit_instr(op, arg, next): # This will be the body of the outer proc, so just "jump" straight to it: start_instr = n # Note: emit_instr would want to choose the label... - emit_rib("main", "#0", start_instr, "#0", comment=f"jump {start_instr}") + # emit_rib("main", "#0", start_instr, "#0", comment=f"jump {start_instr}") + # Note: there is no true no-op, and "id" is not yet initialized. This will leave junk on the + # stack. What we really want is to put the "main" label on start_instr when it's emitted. + emit_rib("main", "#3", "#-42", start_instr) BUILTINS = { @@ -689,11 +692,17 @@ def pc_z_to_d(): asm.instr("D=D-A") asm.instr("@handle_jump_to_slot") asm.instr("D;JLT") - pc_y_to_d() - asm.instr("@PC") - asm.instr("M=D") - asm.instr("@exec_loop") - asm.instr("0;JMP") + + asm.comment("TODO: TEMP_? = target proc, ...") + asm.instr("@halt_loop") + # asm.instr("0;JMP") + # asm.comment("HACK: PC = PC.y; bogus!") + # y_to_d("PC") + # asm.instr("@PC") + # asm.instr("M=D") + # asm.instr("@exec_loop") + # asm.instr("0;JMP") + asm.blank() asm.label("handle_jump_to_slot") asm.comment("TODO") @@ -736,8 +745,7 @@ def pc_z_to_d(): pc_y_to_d() asm.comment("TODO: check tag") asm.comment("Allocate rib: x = pc.y") - rib_append() - asm.comment("y = SP") + rib_append() asm.comment("y = SP") asm.instr("@SP") asm.instr("D=M") rib_append() From 11a2a9052bafe5eb62d73bb6b16991b334901a9d Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Fri, 2 Feb 2024 13:17:44 -0500 Subject: [PATCH 041/221] Inevitable tracing additions --- alt/scheme/rvm.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index effffdd..ac172c2 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -9,7 +9,7 @@ # import nand from nand.translate import AssemblySource from alt import big -from nand.vector import unsigned +from nand.vector import extend_sign, unsigned TRACE_NONE = 0 @@ -22,7 +22,7 @@ """Log every CPU instruction (in addition to COARSE and FINE logging.)""" -def run(program, print_asm=True, trace_level=TRACE_COARSE, verbose_tty=True): +def run(program, print_asm=True, trace_level=TRACE_FINE, verbose_tty=True): encoded = compile(program) # print(f"encoded program: {repr(encoded)}") @@ -110,14 +110,15 @@ def show_target(val): def show_obj(val, deep=True): # FIXME: check tag - if val < big.ROM_BASE: - return f"{val}" + if -big.ROM_BASE < extend_sign(val) < big.ROM_BASE: + return f"{extend_sign(val)}" else: x, y, z = peek(val), peek(val+1), peek(val+2) if deep: - return f"({show_obj(x), show_obj(y), show_obj(z)}" + return f"({show_obj(x, False)}, {show_obj(y, False)}, {show_obj(z, False)})" else: - return f"({x, y, z}" + # return f"{x, y, z}" + return f"(@{unsigned(x)}, @{unsigned(y)}, @{unsigned(z)})" def show_stack(addr): def go(addr): @@ -148,6 +149,13 @@ def show_addr(addr): max_ribs = (big.HEAP_TOP - big.HEAP_BASE)//3 print(f" heap: {current_ribs:3,d} ({100*current_ribs/max_ribs:0.1f}%)") print(f" PC: {show_addr(peek(1))}") + + # HACK? + print(f" symbols (n..0): ({show_addr(peek(4))}) {show_stack(peek(4))}") + print(f" ribs:") + for addr in range(big.HEAP_BASE, unsigned(computer.peek(BUILTINS["NEXT_RIB"])), 3): + print(f" @{addr}; {show_obj(addr, deep=False)}") + print(f" {show_instr(peek(1))}") elif trace_level >= TRACE_FINE and computer.pc in symbols_by_addr: print(f"{cycles:3,d}: ({symbols_by_addr[computer.pc]})") From 71ce188288572289f70a0c22e316ca314c3a3d58 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Fri, 2 Feb 2024 13:19:41 -0500 Subject: [PATCH 042/221] Fix up symbol table initialization, hopefully --- alt/scheme/rvm.py | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index ac172c2..9a1c1f8 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -210,6 +210,12 @@ def decode(input, asm): asm.comment("=== Data ===") + # Exactly one primitive proc rib is pre-defined: `rib` + asm.label("rib_rib") + asm.instr("#0") + asm.instr("#0") + asm.instr("#1") + # Three constants the runtime can refer to by name: def special(name): asm.label(name) @@ -304,7 +310,7 @@ def emit_vector(lbl, elems, count: int): emit_rib(lbl, elems, f"#{count}", "#4" # block of address space? asm.comment("Table of pointers to symbol name ribs in ROM:") asm.label("symbol_names_start") - for lbl, s in sym_names: + for lbl, s in reversed(sym_names): if s != "": asm.comment(f'"{s}"') asm.instr(f"@{lbl}") @@ -332,9 +338,9 @@ def symbol_ref(idx): The table is written from the end, and each entry is made of of two ribs, the `symbol` and a `pair`. """ - # TODO: fix the inevitable of-by-one error(s) here + # TODO: fix the inevitable off-by-one error(s) here asm.comment(f'symbol_ref({idx}); "{sym_names[idx][1]}"') - return f"#{big.HEAP_BASE + 6*(len(sym_names) - idx)}" + return f"#{big.HEAP_BASE + 6*(len(sym_names) - idx - 1)}" def emit_instr(op, arg, next): lbl = asm.next_label("instr") @@ -478,6 +484,7 @@ def interpreter(asm): # FIRST_PRIMITIVE = 8 # LAST_PRIMITIVE = FIRST_PRIMITIVE + num_primitives + RIB_PROC = "rib_rib" FALSE = "rib_false" TRUE = "rib_true" NIL = "rib_nil" @@ -571,6 +578,7 @@ def rib_append(val="D"): asm.instr("D=A") rib_append() asm.instr("@TEMP_0") + asm.instr("A=M") asm.instr("D=M") rib_append() asm.instr("@2") # symbol @@ -620,6 +628,26 @@ def rib_append(val="D"): asm.instr(f"@{symbol_table_loop}") asm.instr("D;JLT") + asm.comment("HACK: initialize standard symbols (rib, false, true, nil)") + def inject_symbol_value(idx, val): + asm.comment(f"global({idx}) = {val}") + asm.instr("@NEXT_RIB") + asm.instr("D=M") + asm.instr(f"@{(idx+1)*6}") + asm.instr("D=D-A") + asm.instr("@TEMP_0") + asm.instr("M=D") + asm.instr("@KEYBOARD"); asm.instr("M=D") # HACK + asm.instr(val) + asm.instr("D=A") + asm.instr("@TEMP_0") + asm.instr("A=M") + asm.instr("M=D") + inject_symbol_value(0, "@rib_rib") + inject_symbol_value(1, "@rib_false") + inject_symbol_value(2, "@rib_true") + inject_symbol_value(3, "@rib_nil") + asm.blank() From 22b3c07f954fd269b23fb43c8da8c112a5963fc7 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Fri, 2 Feb 2024 13:35:55 -0500 Subject: [PATCH 043/221] Implement primitive 0 (rib) --- alt/scheme/rvm.py | 258 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 208 insertions(+), 50 deletions(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 9a1c1f8..e0bf6ea 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -451,15 +451,17 @@ def emit_instr(op, arg, next): "SYMBOL_TABLE": 4, # Only used during intiialization? - # General-purpose temporary storage: + # General-purpose temporary storage for the interpreter/primitives: "TEMP_0": 5, "TEMP_1": 6, + "TEMP_2": 7, + "TEMP_3": 7, # Useful values/addresses: "FIRST_RIB_MINUS_ONE": big.HEAP_BASE-1, "MAX_RIB": big.HEAP_TOP, - # The largest value that can ever by the index of a slot, as opposed to the address of a global (symbol) + # The largest value that can ever be the index of a slot, as opposed to the address of a global (symbol) "MAX_SLOT": big.ROM_BASE-1, } @@ -534,6 +536,21 @@ def rib_append(val="D"): asm.instr("A=M-1") asm.instr(f"M={val}") + def pop(dest): + asm.comment("TODO: check SP[2] == 0") + asm.comment(f"{dest} = SP[0]") + asm.instr("@SP") + asm.instr("A=M") + asm.instr("D=M") + asm.instr(dest) + asm.instr("M=D") + asm.comment("SP = SP[1]") + asm.instr("@SP") + asm.instr("A=M+1") + asm.instr("D=M") + asm.instr("@SP") + asm.instr("M=D") + # TODO: find end of encoded program; initialize stack pointer below it # decode_stack_start = 16384//2 - 1 @@ -673,22 +690,22 @@ def inject_symbol_value(idx, val): # Exec loop: # - def pc_x_to_d(): - """Load the first field of the current instruction to D.""" - asm.instr("@PC") + def x_to_d(rib_addr_loc): + """Load the first field of a rib to D.""" + asm.instr(f"@{rib_addr_loc}") asm.instr("A=M") asm.instr("D=M") - def pc_y_to_d(): - """Load the middle field of the current instruction to D.""" - asm.instr("@PC") + def y_to_d(rib_addr_loc): + """Load the middle field of a rib to D.""" + asm.instr(f"@{rib_addr_loc}") asm.instr("A=M+1") asm.instr("D=M") - def pc_z_to_d(): - """Load the last field of the current instruction to D.""" + def z_to_d(rib_addr_loc): + """Load the last field of a rib to D.""" # Note: could save an instruction here if the pointer pointed to the *middle* field. - asm.instr("@PC") + asm.instr(f"@{rib_addr_loc}") asm.instr("A=M+1") asm.instr("A=A+1") # Cheeky: add two ones instead of using @2 to save a cycle asm.instr("D=M") @@ -699,7 +716,7 @@ def pc_z_to_d(): asm.comment("Typical loop path: get the next instruction to interpret from the third field of the current instruction:") asm.label("continue_next") - pc_z_to_d() + z_to_d("PC") asm.instr("@PC") asm.instr("M=D") @@ -711,19 +728,39 @@ def pc_z_to_d(): asm.label("exec_loop") - asm.comment("Sanity check: negative instruction type is impossible") - pc_x_to_d() + asm.comment("Sanity check: instruction type between 0 and 4") + x_to_d("PC") asm.instr("@halt_loop") asm.instr("D;JLT") - - asm.instr("@code_gt_zero") + asm.instr("@4") + asm.instr("D=D-A") + asm.instr("@halt_loop") asm.instr("D;JGT") + # Note: this indexed jump doesn't seem to save any cycles vs 5 branches, but it's parallel + # to the primitive handler dispatcher, so maybe easier to follow? + asm.comment("indexed jump to instruction handler") + x_to_d("PC") + asm.instr("@opcode_handler_table") + asm.instr("A=A+D") + asm.instr("A=M") + asm.instr("0;JMP") + asm.blank() + + asm.label("opcode_handler_table") + asm.instr("@opcode_0") + asm.instr("@opcode_1") + asm.instr("@opcode_2") + asm.instr("@opcode_3") + asm.instr("@opcode_4") + asm.blank() + + asm.label("opcode_0") asm.comment("type 0: jump/call") - pc_z_to_d() + z_to_d("PC") asm.instr("@handle_call") asm.instr("D;JNE") - pc_y_to_d() + y_to_d("PC") asm.instr(f"@MAX_SLOT") asm.instr("D=D-A") asm.instr("@handle_jump_to_slot") @@ -744,49 +781,139 @@ def pc_z_to_d(): asm.comment("TODO") asm.instr("@halt_loop") asm.instr("0;JMP") - + asm.blank() asm.label("handle_call") + y_to_d("PC") + asm.instr(f"@MAX_SLOT") + asm.instr("D=D-A") + asm.instr("@handle_call_to_slot") + asm.instr("D;JLT") + + asm.comment("TEMP_3 = proc rib from symbol") + y_to_d("PC") + asm.instr("A=D") # HACK + asm.instr("D=M") + asm.instr("@TEMP_3") + asm.instr("M=D") + asm.instr("@handle_call_start") + asm.instr("0;JMP") + asm.blank() + + asm.label("handle_call_to_slot") + asm.comment("TEMP_3 = proc rib from stack") asm.comment("TODO") asm.instr("@halt_loop") asm.instr("0;JMP") + asm.blank() + asm.comment("call protocol:") - asm.label("code_gt_zero") - asm.instr("D=D-1") - asm.instr("@code_gt_one") - asm.instr("D;JGT") + asm.label("handle_call_start") + + asm.comment("Check is proc rib:") + asm.instr("@TEMP_3") + asm.instr("A=M") + asm.instr("A=A+1") + asm.instr("A=A+1") + asm.instr("D=M-1") + asm.instr("@halt_loop") + asm.instr("D;JNE") + + asm.comment("Check primitive or closure:") + asm.instr("@TEMP_3") + asm.instr("A=M") + asm.instr("D=M") + asm.instr("@0x1F") # Mask off bits that are zero iff it's a primitive + asm.instr("A=!A") + asm.instr("D=D&A") + asm.instr("@handle_call_closure") + asm.instr("D;JNE") + + asm.label("handle_call_primitive") + asm.instr("@TEMP_3") + asm.instr("A=M") + asm.instr("D=M") + asm.instr("@primitive_vector_table_start") + asm.instr("A=A+D") + asm.instr("A=M") + asm.instr("0;JMP") + asm.blank() + + asm.label("handle_call_closure") + asm.comment("R5 = new rib for the continuation") + asm.instr("@NEXT_RIB") + asm.instr("D=M") + asm.instr("@TEMP_0") + rib_append(0) + rib_append(0) + rib_append(0) + asm.blank() + + asm.comment("- pop num_params values from stack, forming list with cont as tail:") # re-use the ribs? + asm.comment("R6 = num_params") + y_to_d("PC") # target proc + asm.instr("@TEMP_1") + asm.instr("M=D") + asm.comment("R7 = acc") + asm.instr("@TEMP_0") + asm.instr("D=M") + asm.instr("@TEMP_2") + asm.instr("M=D") + + asm.label("pop_params_loop") + asm.instr("@TEMP_1") + asm.instr("D=M") + asm.instr("@pop_params_end") + asm.instr("D;JLE") + + + + asm.instr("@TEMP_1") + asm.instr("M=M-1") + asm.instr("@pop_params_loop") + asm.instr("0;JMP") + asm.label("pop_params_end") + asm.blank() + + asm.comment("- cont.x = SP") + asm.comment("- cont.y = PC.y (proc)") + asm.comment("- cont.z = PC.z") + asm.comment("- SP = arg_list") + asm.comment("- PC = PC.y.z (proc entry point)") + + + asm.instr("@continue_next") + asm.instr("0;JMP") + asm.blank() + asm.label("opcode_1") asm.comment("type 1: set") asm.comment("TODO") asm.instr("@halt_loop") asm.instr("0;JMP") + asm.blank() - asm.label("code_gt_one") - asm.instr("D=D-1") - asm.instr("@code_gt_two") - asm.instr("D;JGT") - + asm.label("opcode_2") asm.comment("type 2: get") asm.comment("TODO") asm.instr("@halt_loop") asm.instr("0;JMP") + asm.blank() - asm.label("code_gt_two") - asm.instr("D=D-1") - asm.instr("@code_gt_three") - asm.instr("D;JGT") - + asm.label("opcode_3") asm.comment("type 3: const") - pc_y_to_d() + y_to_d("PC") asm.comment("TODO: check tag") asm.comment("Allocate rib: x = pc.y") - rib_append() asm.comment("y = SP") + rib_append() + asm.comment("y = SP") asm.instr("@SP") asm.instr("D=M") rib_append() asm.comment("z = 0 (pair)") rib_append("0") + asm.blank() asm.comment("SP = just-allocated rib") asm.instr("@NEXT_RIB") @@ -795,26 +922,19 @@ def pc_z_to_d(): asm.instr("D=D-A") asm.instr("@SP") asm.instr("M=D") + asm.blank() asm.instr("@continue_next") asm.instr("0;JMP") + asm.blank() - asm.label("code_gt_three") - asm.instr("D=D-1") - asm.instr("@halt_loop") - asm.instr("D;JGT") - + asm.label("opcode_4") asm.comment("type 4: if") asm.comment("TODO") asm.instr("@halt_loop") asm.instr("0;JMP") - asm.blank() - # asm.comment("TEMP: just go to 'next' no matter what") - # asm.instr("@continue_next") - # asm.instr("0;JMP") - # # Halt: @@ -832,11 +952,39 @@ def pc_z_to_d(): asm.label("primitive_rib") asm.comment("primitive 0; rib :: x y z -- rib(x, y, z)") - asm.comment("TODO:") - # asm.comment("- check for stack-heap collision") - # asm.comment("- allocate space for new rib") - # asm.comment("- pop three values and copy them into the new rib") - asm.instr("@exec_loop") + + pop("@TEMP_2") + pop("@TEMP_1") + pop("@TEMP_0") + asm.instr("@TEMP_0") + asm.instr("D=M") + rib_append() + asm.instr("@TEMP_1") + asm.instr("D=M") + rib_append() + asm.instr("@TEMP_2") + asm.instr("D=M") + rib_append() + + # TODO: pop only two, then modify the top of stack in place to save one allocation + asm.comment("push allocated rib") + asm.instr("@NEXT_RIB") + asm.instr("D=M") + asm.instr("@3") + asm.instr("D=D-A") + rib_append() + asm.instr("@SP") + asm.instr("D=M") + rib_append() + rib_append("0") # pair + asm.instr("@NEXT_RIB") + asm.instr("D=M") + asm.instr("@3") + asm.instr("D=D-A") + asm.instr("@SP") + asm.instr("M=D") + + asm.instr("@continue_next") asm.instr("0;JMP") asm.blank() @@ -937,6 +1085,16 @@ def pc_z_to_d(): asm.comment("22") asm.instr("@primitive_halt") # fatal? + asm.comment("dummy handlers for 23-31 to simplify range check above:") + asm.instr("@primitive_unimp") + asm.instr("@primitive_unimp") + asm.instr("@primitive_unimp") + asm.instr("@primitive_unimp") + asm.instr("@primitive_unimp") + asm.instr("@primitive_unimp") + asm.instr("@primitive_unimp") + asm.instr("@primitive_unimp") + asm.instr("@primitive_unimp") asm.label("primitive_vectors_end") asm.blank() \ No newline at end of file From b05030038377662eacc6c620892f8796c0926eaa Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Fri, 2 Feb 2024 13:39:32 -0500 Subject: [PATCH 044/221] Tracing again --- alt/scheme/rvm.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index e0bf6ea..73acdb5 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -150,14 +150,14 @@ def show_addr(addr): print(f" heap: {current_ribs:3,d} ({100*current_ribs/max_ribs:0.1f}%)") print(f" PC: {show_addr(peek(1))}") - # HACK? - print(f" symbols (n..0): ({show_addr(peek(4))}) {show_stack(peek(4))}") - print(f" ribs:") - for addr in range(big.HEAP_BASE, unsigned(computer.peek(BUILTINS["NEXT_RIB"])), 3): - print(f" @{addr}; {show_obj(addr, deep=False)}") + # # HACK? + # print(f" symbols (n..0): ({show_addr(peek(4))}) {show_stack(peek(4))}") + # print(f" ribs:") + # for addr in range(big.HEAP_BASE, unsigned(computer.peek(BUILTINS["NEXT_RIB"])), 3): + # print(f" @{addr}; {show_obj(addr, deep=False)}") print(f" {show_instr(peek(1))}") - elif trace_level >= TRACE_FINE and computer.pc in symbols_by_addr: + elif trace_level >= TRACE_FINE and computer.pc in symbols_by_addr and symbols_by_addr[computer.pc] != "halt_loop": print(f"{cycles:3,d}: ({symbols_by_addr[computer.pc]})") elif trace_level >= TRACE_ALL: print(f"{cycles:3,d}: {computer.pc}") From bc52475ac056dfedc9e2cb46828b009674d337ff Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Fri, 2 Feb 2024 13:39:43 -0500 Subject: [PATCH 045/221] Cleanup --- alt/scheme/rvm.py | 55 ----------------------------------------------- 1 file changed, 55 deletions(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 73acdb5..3c35771 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -472,58 +472,11 @@ def interpreter(asm): This part of the ROM is the same, independent of the Scheme program that's being interpreted. """ - # Interpreter variables in low memory: - # SP = 0 - # PC = 1 - # NEXT_RIB = 2 - - # RETURN = 3 - - # used only during initialization? - # SYMBOL_TABLE = 4 - - num_primitives = 22 - # FIRST_PRIMITIVE = 8 - # LAST_PRIMITIVE = FIRST_PRIMITIVE + num_primitives - RIB_PROC = "rib_rib" FALSE = "rib_false" TRUE = "rib_true" NIL = "rib_nil" - # FIXME: probably no return; jump straight to exec_loop? - # def call_primitive(lbl): - # rtn_label = asm.next_label("return") - # asm.instr(f"@{rtn_label}") - # asm.instr("D=A") - # asm.instr(f"@{RETURN}") - # asm.instr("M=D") - # asm.instr(f"@{lbl}") - # asm.instr("0;JMP") - # asm.label(rtn_label) - - # def return_from_primitive(): - # asm.comment("return") - # asm.instr(f"@{RETURN}") - # asm.instr("A=M") - # asm.instr("0;JMP") - - # def push_d(): - # """Push the value in D onto the stack, without over-writing it.""" - # asm.instr(f"@{SP}") - # asm.instr("M=M-1") - # asm.instr("A=M+1") # i.e., point to the previous location - # asm.instr("M=D") - - # def rib(x, y, z): - # asm.comment(f"rib: {x}, {y}, {z}") - # asm.comment("TODO: D = {x}") - # asm.instr("@{NEXT_RIB}") - # asm.instr("A=M") - # asm.instr("M=D") - # asm.comment("TODO: D = {y}") - # asm.instr("@{NEXT_RIB}") - # asm.instr("AM=M+1") def rib_append(val="D"): """Add a word to the rib currently being constructed. @@ -552,14 +505,6 @@ def pop(dest): asm.instr("M=D") - # TODO: find end of encoded program; initialize stack pointer below it - # decode_stack_start = 16384//2 - 1 - # asm.comment("SP = below the encoded program") - # asm.instr(f"@{decode_stack_start}") - # asm.instr("D=A") - # asm.instr(f"@{SP}") - # asm.instr("M=D") - asm.label("start") asm.comment("NEXT_RIB = FIRST_RIB (HEAP_BASE)") asm.instr("@FIRST_RIB_MINUS_ONE") # Note: this value is one lower than the actual start of the heap, to fit in 15 bits From 9a999417dd8d187fcf4a56d533ec791302d27f49 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sat, 3 Feb 2024 15:36:26 -0500 Subject: [PATCH 046/221] Fix tracing; --- alt/big.py | 8 ++++++-- alt/scheme/rvm.py | 39 +++++++++++++++++++++------------------ 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/alt/big.py b/alt/big.py index aa39bc8..71445fc 100755 --- a/alt/big.py +++ b/alt/big.py @@ -281,12 +281,13 @@ def run(program, chip=BigComputer, name="Flat!", font="monaco-9", halt_addr=None kvm = TextKVM(name, 80, 25, 6, 10, "alt/big/Monaco9.png") # TODO: use computer.py's "run", for many more features + cycles = 0 halted = False while True: if not halted and computer.pc == halt_addr: halted = True - print(f"halted after {cycles} cycles") + print(f"\nHalted after {cycles} cycles\n") trace(computer, cycles) @@ -297,12 +298,15 @@ def run(program, chip=BigComputer, name="Flat!", font="monaco-9", halt_addr=None cycles_per_call = 10 # has to be a factor of CYCLES_PER_UPDATE computer.ticktock(cycles_per_call) cycles += cycles_per_call + + if computer.fetch and trace is not None: + trace(computer, cycles) + else: time.sleep(0.1) if trace is not None: - trace(computer, cycles) CYCLES_PER_UPDATE = 2 # HACK: to ensure we see all the TTY output else: CYCLES_PER_UPDATE = 100 diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 3c35771..7fb6d78 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -74,10 +74,6 @@ def show_map(label, m): def trace(computer, cycles): nonlocal last_traced_exec - # Only log on one of the half-cycles: - if not computer.fetch: - return - def peek(addr): """Read from RAM or ROM.""" if big.ROM_BASE <= addr < big.HEAP_BASE: @@ -122,9 +118,9 @@ def show_obj(val, deep=True): def show_stack(addr): def go(addr): - if addr == symbols["rib_nil"]: + if addr == symbols["rib_nil"]: return [] - elif peek(addr+2) == 0: # pair + elif peek(addr+2) == 0: # pair return go(peek(addr+1)) + [show_obj(peek(addr))] return ", ".join(go(addr)) @@ -135,7 +131,8 @@ def show_addr(addr): else: return f"@{unsigned(addr)}" - if trace_level >= TRACE_COARSE and computer.pc == symbols["exec_loop"]: + if (trace_level >= TRACE_COARSE + and (computer.pc == symbols["exec_loop"] or computer.pc == symbols["halt_loop"])): if last_traced_exec is None: print(f"{cycles:,d}:") else: @@ -529,11 +526,15 @@ def pop(dest): symbol_table_loop = "symbol_table_loop" asm.label(symbol_table_loop) - asm.comment("DEBUG: log the pointer to tty") - asm.instr("@TEMP_0") - asm.instr("D=M") - asm.instr("@KEYBOARD") - asm.instr("M=D") + # asm.comment("DEBUG: log the pointer to tty") + # asm.instr("@TEMP_0") + # asm.instr("D=M") + # asm.instr("@KEYBOARD") + # asm.instr("M=D") + # asm.instr("A=D") + # asm.instr("D=M") + # asm.instr("@KEYBOARD") + # asm.instr("M=D") asm.comment("new symbol, with value = false") asm.instr("@rib_false") @@ -573,11 +574,11 @@ def pop(dest): asm.instr("@SYMBOL_TABLE") asm.instr("M=D") - asm.comment("DEBUG: log NEXT_RIB to tty") - asm.instr("@NEXT_RIB") - asm.instr("D=M") - asm.instr("@KEYBOARD") - asm.instr("M=D") + # asm.comment("DEBUG: log NEXT_RIB to tty") + # asm.instr("@NEXT_RIB") + # asm.instr("D=M") + # asm.instr("@KEYBOARD") + # asm.instr("M=D") asm.comment("increment R5") asm.instr("@TEMP_0") @@ -590,6 +591,8 @@ def pop(dest): asm.instr(f"@{symbol_table_loop}") asm.instr("D;JLT") + asm.blank() + asm.comment("HACK: initialize standard symbols (rib, false, true, nil)") def inject_symbol_value(idx, val): asm.comment(f"global({idx}) = {val}") @@ -599,7 +602,7 @@ def inject_symbol_value(idx, val): asm.instr("D=D-A") asm.instr("@TEMP_0") asm.instr("M=D") - asm.instr("@KEYBOARD"); asm.instr("M=D") # HACK + # asm.instr("@KEYBOARD"); asm.instr("M=D") # HACK asm.instr(val) asm.instr("D=A") asm.instr("@TEMP_0") From e50869e3e0edc15d1e5223e5e975b8c87c524b63 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sat, 3 Feb 2024 15:37:08 -0500 Subject: [PATCH 047/221] =?UTF-8?q?Skip=20the=20=E2=80=9Cmain=E2=80=9D=20i?= =?UTF-8?q?nstruction=20in=20a=20cleaner=20way?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- alt/scheme/rvm.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 7fb6d78..d94dc34 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -626,7 +626,9 @@ def inject_symbol_value(idx, val): asm.instr("@SP") asm.instr("M=D") - asm.comment("PC = @main") + # Note main is a meaningless instruction with its next the actual entry point, so it's actually + # skipped on the way into the interpreter loop. + asm.comment("PC = @main[2]") asm.instr("@main") asm.instr("D=A") asm.instr("@PC") @@ -658,9 +660,7 @@ def z_to_d(rib_addr_loc): asm.instr("A=A+1") # Cheeky: add two ones instead of using @2 to save a cycle asm.instr("D=M") - asm.comment("First time: jump to the main loop") - asm.instr("@exec_loop") - asm.instr("0;JMP") + asm.comment("First time: start with the 'next' of main") asm.comment("Typical loop path: get the next instruction to interpret from the third field of the current instruction:") asm.label("continue_next") From 205d18807d199dbc447f0a1b7378c0ff17a84356 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 4 Feb 2024 12:42:37 -0500 Subject: [PATCH 048/221] Factor out instruction decoding Simplifies the CPU somewhat, and can be shared by other implementations. --- alt/big.py | 92 ++++++++++++++++++++++++++++++++++++++++--------- alt/test_big.py | 87 +++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 158 insertions(+), 21 deletions(-) diff --git a/alt/big.py b/alt/big.py index 71445fc..04e7315 100755 --- a/alt/big.py +++ b/alt/big.py @@ -102,6 +102,67 @@ def FlatMemory(inputs, outputs): outputs.tty_ready = tty.ready +# TODO: move this to solved_05 as the standard impl +@chip +def DecodeALU(inputs, outputs): + """Inspect the bits of an instruction word and determine if it involves the ALU and if so + what the control signals should be. + + When `is_alu_instr` is high, the rest of the signals should be wired to the ALU and PC. + + This decoding is intended to be independent of architectural concerns such as cycle timing, + memory mapping, etc., so it could be used in a variety of implementations. + + Note: there's almost no logic here, so just a handful of gates, but breaking out the signals + and providing clear names for them can make the various CPU implementations easier to follow. + """ + + instruction = inputs.instruction + + i, _, _, a, c5, c4, c3, c2, c1, c0, da, dd, dm, _, _, _ = [instruction[j] for j in reversed(range(16))] + + # If low, then all the other output signals should be ignored: + outputs.is_alu_instr = i + + # ALU control: + outputs.mem_to_alu = a # if low, the ALU gets the value of A; if high, the value from memory + outputs.zx = c5 + outputs.nx = c4 + outputs.zy = c3 + outputs.ny = c2 + outputs.f = c1 + outputs.no = c0 + + # ALU Destinations: + # Note: these are all low when the ALU is not in effect, so it's safe to use them directly + outputs.alu_to_a = And(a=i, b=da).out + outputs.alu_to_d = And(a=i, b=dd).out + outputs.alu_to_m = And(a=i, b=dm).out + + +# TODO: move this to solved_05 as the standard impl +@chip +def DecodeJump(inputs, outputs): + """Inspect the bits of an instruction word and the control signals from the ALU and determine + if a branch should be taken. + + Note: if the instruction isn't an ALU instruction, then the output is low. + """ + + instruction = inputs.instruction + ng = inputs.ng + zr = inputs.zr + + i, _, _, _, _, _, _, _, _, _, _, _, _, jlt, jeq, jgt = [instruction[j] for j in reversed(range(16))] + + # TODO: this seems like a lot of gates to transform 5 bits of input into one bit of output. + # Hmm, is 16 a lot? + outputs.jump = And(a=i, + b=Or(a=Or(a=And(a=jlt, b=ng).out, + b=And(a=jeq, b=zr).out).out, + b=And(a=jgt, b=And(a=Not(in_=ng).out, b=Not(in_=zr).out).out).out).out).out + + @chip def IdlableCPU(inputs, outputs): """Same as the standard CPU, plus an 'idle' input that suspends all state updates.""" @@ -114,32 +175,29 @@ def IdlableCPU(inputs, outputs): # Extra for fetch/execute cycles: idle = inputs.idle # When set, *don't* update any state - i, _, _, a, c5, c4, c3, c2, c1, c0, da, dd, dm, jlt, jeq, jgt = [instruction[j] for j in reversed(range(16))] + decode = DecodeALU(instruction=instruction) - not_i = Not(in_=i).out + is_imm = Not(in_=decode.is_alu_instr).out not_idle = Not(in_=idle).out alu = lazy() - a_reg = Register(in_=Mux16(a=instruction, b=alu.out, sel=i).out, - load=And(a=not_idle, b=Or(a=not_i, b=da).out).out) + a_reg = Register(in_=Mux16(a=instruction, b=alu.out, sel=decode.is_alu_instr).out, + load=And(a=not_idle, b=Or(a=is_imm, b=decode.alu_to_a).out).out) d_reg = Register(in_=alu.out, - load=And(a=not_idle, b=And(a=i, b=dd).out).out) - jump_lt = And(a=alu.ng, b=jlt).out - jump_eq = And(a=alu.zr, b=jeq).out - jump_gt = And(a=And(a=Not(in_=alu.ng).out, b=Not(in_=alu.zr).out).out, b=jgt).out - jump = And(a=i, - b=Or(a=jump_lt, b=Or(a=jump_eq, b=jump_gt).out).out - ).out + load=And(a=not_idle, b=decode.alu_to_d).out) + + jump = DecodeJump(instruction=instruction, ng=alu.ng, zr=alu.zr).jump + pc = PC(in_=a_reg.out, load=And(a=not_idle, b=jump).out, inc=not_idle, reset=reset) - alu.set(ALU(x=d_reg.out, y=Mux16(a=a_reg.out, b=inM, sel=a).out, - zx=c5, nx=c4, zy=c3, ny=c2, f=c1, no=c0)) + alu.set(ALU(x=d_reg.out, y=Mux16(a=a_reg.out, b=inM, sel=decode.mem_to_alu).out, + zx=decode.zx, nx=decode.nx, zy=decode.zy, ny=decode.ny, f=decode.f, no=decode.no)) - outputs.outM = alu.out # M value output - outputs.writeM = And(a=dm, b=i).out # Write to M? - outputs.addressM = a_reg.out # Address in data memory (of M) (latched) - outputs.pc = pc.out # address of next instruction (latched) + outputs.outM = alu.out # M value output + outputs.writeM = decode.alu_to_m # Write to M? + outputs.addressM = a_reg.out # Address in data memory (of M) (latched) + outputs.pc = pc.out # address of next instruction (latched) @chip diff --git a/alt/test_big.py b/alt/test_big.py index 44434c1..53d01d5 100755 --- a/alt/test_big.py +++ b/alt/test_big.py @@ -153,8 +153,85 @@ def test_gates_mem(): assert gate_count(project_05.MemorySystem)['nands'] == 163 -# def test_cpu(chip=project_05.CPU): -# cpu = run(chip) +def test_decode_alu(): + decode = nand.run(DecodeALU) + + + # A typical computation with memory access: + decode.instruction = parse_op("M=M-D") + + assert decode.is_alu_instr + + assert decode.mem_to_alu + + assert not decode.zx + assert not decode.nx + assert not decode.zy + assert decode.ny + assert decode.f + assert decode.no + + assert not decode.alu_to_a + assert not decode.alu_to_d + assert decode.alu_to_m + + + # A typical conditional branch: + decode.instruction = parse_op("D;JLE") + + assert decode.is_alu_instr + + # assert decode.mem_to_alu # unused + + assert not decode.zx + assert not decode.nx + assert decode.zy + assert decode.ny + assert not decode.f + assert not decode.no + + assert not decode.alu_to_a + assert not decode.alu_to_d + assert not decode.alu_to_m + + + decode.instruction = parse_op("@1234") + + assert not decode.is_alu_instr + + assert not decode.alu_to_a + assert not decode.alu_to_d + assert not decode.alu_to_m + + +def test_gates_decode_alu(): + assert gate_count(DecodeALU)['nands'] == 6 + + +def test_decode_jump(): + decode = nand.run(DecodeJump) + + + # A typical computation with memory access: + decode.instruction = parse_op("M=M-D") + + assert not decode.jump + + + # A typical conditional branch: + decode.instruction = parse_op("D;JLE") + decode.ng = 1 + decode.zr = 0 + + assert decode.jump + + +def test_gates_decode_jump(): + assert gate_count(DecodeJump)['nands'] == 18 + + +# def test_cpu(chip=BigCPU): +# cpu = nand.run(chip) # cpu.instruction = 0b0011000000111001 # @12345 # cpu.ticktock() @@ -350,7 +427,8 @@ def test_gates_mem(): def test_gates_cpu(): """Portion of the extra chip size that's in the CPU's "idle" state.""" - assert gate_count(IdlableCPU)['nands'] == 1106 + # Note: factoring out instruction decoding seems to have added 2 gates + assert gate_count(IdlableCPU)['nands'] == 1108 import project_05 assert gate_count(project_05.CPU)['nands'] == 1099 @@ -595,7 +673,8 @@ def test_computer_no_program(): def test_gates_computer(): """Overall extra chip size.""" - assert gate_count(BigComputer)['nands'] == 1448 + # Note: factoring out instruction decoding seems to have added 2 gates + assert gate_count(BigComputer)['nands'] == 1450 import project_05 assert gate_count(project_05.Computer)['nands'] == 1262 From 37200ee6fb9ef327dc444a387ed96f4f69454bf6 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 4 Feb 2024 12:46:32 -0500 Subject: [PATCH 049/221] Mostly meaningful description --- alt/scheme/README.md | 95 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 alt/scheme/README.md diff --git a/alt/scheme/README.md b/alt/scheme/README.md new file mode 100644 index 0000000..d79f8f6 --- /dev/null +++ b/alt/scheme/README.md @@ -0,0 +1,95 @@ +# Scheme + +Scheme interpreter, based on [Ribbit](https://github.com/udem-dlteam/ribbit/). + +Goals: +- run on the vanilla Hack architecture (well, the CPU anyway) +- provide a REPL that can compile and run simple functions like `fib` or `fact` written at the + keyboard +- run something graphical (even if in character-mode) +- implement as little as possible in assembly; just the bare interpreter and Ribbit primitives, + plus a few additions for accessing the hardware. + + +## Virtual Machine + +A source program is compiled by the Ribbit AOT compiler to an instruction graph as specified in +[A Small Scheme VM, Compiler, and REPL in 4K](http://www.iro.umontreal.ca/~feeley/papers/YvonFeeleyVMIL21.pdf), +with a few modifications. + +Unimplemented primitives: +- `*`, `quotient`: the Hack CPU does not provide these; they can be implemented as ordinary + functions (*TBD* probably need at least `*` as a primitive for speed). +- `getchar` and `putchar`: there is no OS to provide these, they will be implemented as ordinary + functions when needed (i.e. for the REPL) + +Additional primitives: +- `peek`; code: `20`; `x ← pop(); r ← RAM[x]` +- `poke`; code: `21`; `y ← pop(); x ← pop(); RAM[x] ← y; r <- y` +- `halt`; code: `22`; stop the machine, probably by going into a tight loop + +`peek` and `poke` can be used to read and write to any address, but most usefully the screen buffer +(which is at 0x0400–0x0x07E7) and the location mapped to the keyboard (0x07FF). + +`halt` can be used to signal normal completion of a program. + +## Memory Layout + +| Address | Contents | Description | +| 0 | SP | Address of the cons list that represents the stack: i.e. a "pair" rib which contains the value at the top of the stack and points to the next entry. | +| 1 | PC | Address of the rib currently being interpreted. | +| 2 | NEXT_RIB | Address where the next allocation will occur. | +| ... | | TBD: values used by the garbage collector to keep track of free space. | + + +## Rib Representation + +For simplicity, ribs are stored as described in the paper, three words each. + +The high bit of each word is used as a tag to identify whether the word points to a rib. +- A word with the high bit set is a 15-bit signed integer value. To recover an ordinary 16-bit value when + needed, bit 14 is copied to bit 15. For many operations, it's sufficient to treat values as unsigned + and just make sure to set the high bit if it might have been cleared (e.g. due to overflow.) +- A word with the high bit unset is the address of a rib in memory, treated as unsigned and + divided by three, so that every possible rib address fits in the range of 15-bit unsigned values. + +Note: Ribbit's C implementation uses the *low* bit to tag integers, but that's not practical on this +CPU, which does not provide a right-shift instruction. Masking off the high bit can be done efficiently. +On the other hand, rib addresses only ever need to be generated incrementally so we only need the +`extract` operation: multiply by 3 (with two "+" instructions.) + +Future: +- really squeeze the bits and get each rib into 2 words. If possible, this saves 33% of the space, + and adds a *lot* of cycles to decode the bits in the interpreter. And it might mean reducing + the maximum number of ribs that can be addressed, which defeats the purpose. + + +## Initialization + +To make efficient use of available memory, the symbol table and instruction graph produced by +Ribbit's compiler (`rsc.py`) are decoded into ribs and included as "data" words in the ROM, using +[big.py](../big.py)'s extra `#` opcode. + +The actual `symbol` ribs that make up the symbol table have to be mutable, so they're constructed +in RAM during initialization. Their addresses are known ahead of time, so the code in ROM refers to +them directly. + +Note: the REPL consumes about 2K ribs in memory, and takes about 2KB of encoded data in the Ribbit +implementations. That means to initialize we need something like 6K of heap plus 2K words, plus +some for stack. + +Actual size in the implementation: +- Instruction ribs in the ROM for the REPL: 1643 (4.8K words) +- Initial ribs in RAM for the symbol table: 212 (0.6K words) +- Note: this does not account for additional Scheme code that will eventually be needed to make it + work (e.g. getchar/putchar). + +[A comment in the Ribbit source]((https://github.com/udem-dlteam/ribbit/blob/dev/src/host/c/rvm.c#L207)) +suggests that space for 48,000 ribs is needed to bootstrap. Presumably that refers to running the +*compiler*, which is out of scope. + +## GC + +The program runs until memory is exhausted. + +TODO: collect garbage. From 2470bd9ea8002d74c292eaa539a5666d71c5996f Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 4 Feb 2024 17:09:17 -0500 Subject: [PATCH 050/221] Fix rvm.run() --- alt/scheme/rvm.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index d94dc34..6e92b6a 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -23,10 +23,14 @@ def run(program, print_asm=True, trace_level=TRACE_FINE, verbose_tty=True): - encoded = compile(program) # print(f"encoded program: {repr(encoded)}") + run_compiled(encoded, print_asm, trace_level, verbose_tty) + + +def run_compiled(encoded, print_asm=True, trace_level=TRACE_FINE, verbose_tty=True): + asm = AssemblySource() interpreter(asm) From b57ef0d1114577263838c3c4817bf0b6d4af90f7 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 4 Feb 2024 17:15:54 -0500 Subject: [PATCH 051/221] Implement set global --- alt/scheme/rvm.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 6e92b6a..2a87975 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -841,6 +841,28 @@ def z_to_d(rib_addr_loc): asm.label("opcode_1") asm.comment("type 1: set") + y_to_d("PC") + asm.instr(f"@MAX_SLOT") + asm.instr("D=D-A") + asm.instr("@handle_set_slot") + asm.instr("D;JLT") + + asm.label("handle_set_global") + asm.comment("R5 = address of symbol rib") + y_to_d("PC") + asm.instr("@TEMP_0") + asm.instr("M=D") + + asm.comment("RAM[TEMP_0] = pop()") + pop("D") + asm.instr("@TEMP_0") + asm.instr("A=M") + asm.instr("M=D") + asm.instr("@continue_next") + asm.instr("0;JMP") + asm.blank() + + asm.label("handle_set_slot") asm.comment("TODO") asm.instr("@halt_loop") asm.instr("0;JMP") From f40c80db9c3438a46782f22e0a3f1ac9ee7d0165 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 4 Feb 2024 17:17:16 -0500 Subject: [PATCH 052/221] Initialize the stack with an outer continuation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using the previously secret “halt” opcode --- alt/scheme/rvm.py | 43 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 2a87975..5d33f35 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -123,9 +123,16 @@ def show_obj(val, deep=True): def show_stack(addr): def go(addr): if addr == symbols["rib_nil"]: - return [] + raise Exception("Unexpected nil in stack") elif peek(addr+2) == 0: # pair return go(peek(addr+1)) + [show_obj(peek(addr))] + elif peek(addr) == 0 and peek(addr+1) == 0: + # This must be the primordial continuation rib: (0, 0, halt) + # return ["⊥"] + return [] + else: + # A continuation: (stack, closure, next instr) + return go(peek(addr)) + [f"cont(@{peek(addr+2)})"] return ", ".join(go(addr)) def show_addr(addr): @@ -319,10 +326,14 @@ def emit_vector(lbl, elems, count: int): emit_rib(lbl, elems, f"#{count}", "#4" asm.blank() + emit_rib("rib_outer_cont", "#0", "#0", "@instr_halt", "Bottom of stack: continuation to halt") + # Decode RVM instructions: asm.comment("Instructions:") + emit_rib("instr_halt", "#5", "#0", "#0", "halt (secret opcode)") + stack = None def pop(): nonlocal stack @@ -413,7 +424,8 @@ def emit_instr(op, arg, next): break else: params_lbl = asm.next_label("params") - emit_rib(params_lbl, n, "#0", body) + print(f"{repr(n)}; {body}") + emit_rib(params_lbl, f"#{n}", "#0", body) # FIXME: is this even close? proc_label = asm.next_label("proc") asm.label(proc_label) @@ -597,6 +609,7 @@ def pop(dest): asm.blank() + # TODO: fold this into the table of symbol names; cleaner and smaller asm.comment("HACK: initialize standard symbols (rib, false, true, nil)") def inject_symbol_value(idx, val): asm.comment(f"global({idx}) = {val}") @@ -624,15 +637,17 @@ def inject_symbol_value(idx, val): # Initialize interpreter state: # - asm.comment("SP = '()") - asm.instr("@rib_nil") + asm.comment("SP = primordial continuation rib") + asm.instr("@rib_outer_cont") asm.instr("D=A") asm.instr("@SP") asm.instr("M=D") - # Note main is a meaningless instruction with its next the actual entry point, so it's actually + asm.blank() + + # Note: main is a meaningless instruction with its next the actual entry point, so it's actually # skipped on the way into the interpreter loop. - asm.comment("PC = @main[2]") + asm.comment("PC = @main") asm.instr("@main") asm.instr("D=A") asm.instr("@PC") @@ -664,7 +679,7 @@ def z_to_d(rib_addr_loc): asm.instr("A=A+1") # Cheeky: add two ones instead of using @2 to save a cycle asm.instr("D=M") - asm.comment("First time: start with the 'next' of main") + asm.comment("First time: start with the 'next' of main (by falling through to continue_next)") asm.comment("Typical loop path: get the next instruction to interpret from the third field of the current instruction:") asm.label("continue_next") @@ -680,11 +695,12 @@ def z_to_d(rib_addr_loc): asm.label("exec_loop") - asm.comment("Sanity check: instruction type between 0 and 4") + # TODO: if CHECK: + asm.comment("Sanity check: instruction type between 0 and 5") x_to_d("PC") asm.instr("@halt_loop") asm.instr("D;JLT") - asm.instr("@4") + asm.instr("@5") asm.instr("D=D-A") asm.instr("@halt_loop") asm.instr("D;JGT") @@ -705,6 +721,7 @@ def z_to_d(rib_addr_loc): asm.instr("@opcode_2") asm.instr("@opcode_3") asm.instr("@opcode_4") + asm.instr("@opcode_5") asm.blank() asm.label("opcode_0") @@ -909,6 +926,14 @@ def z_to_d(rib_addr_loc): asm.instr("0;JMP") asm.blank() + # Note: a safety check would have the same result, but this makes it explicit in case we ever + # make those checks optional. + asm.label("opcode_5") + asm.comment("type 5: halt") + asm.instr("@halt_loop") + asm.instr("0;JMP") + asm.blank() + # # Halt: From 419f4e548e460099ed259eddfee1a28d4c2fcc94 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 4 Feb 2024 20:37:47 -0500 Subject: [PATCH 053/221] Fix set (global) --- alt/scheme/rvm.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 5d33f35..cc399fa 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -508,7 +508,7 @@ def pop(dest): asm.instr("@SP") asm.instr("A=M") asm.instr("D=M") - asm.instr(dest) + asm.instr(f"@{dest}") asm.instr("M=D") asm.comment("SP = SP[1]") asm.instr("@SP") @@ -869,9 +869,10 @@ def z_to_d(rib_addr_loc): y_to_d("PC") asm.instr("@TEMP_0") asm.instr("M=D") - asm.comment("RAM[TEMP_0] = pop()") - pop("D") + pop("TEMP_1") + asm.instr("@TEMP_1") + asm.instr("D=M") asm.instr("@TEMP_0") asm.instr("A=M") asm.instr("M=D") @@ -951,10 +952,9 @@ def z_to_d(rib_addr_loc): asm.label("primitive_rib") asm.comment("primitive 0; rib :: x y z -- rib(x, y, z)") - - pop("@TEMP_2") - pop("@TEMP_1") - pop("@TEMP_0") + pop("TEMP_2") + pop("TEMP_1") + pop("TEMP_0") asm.instr("@TEMP_0") asm.instr("D=M") rib_append() From 4b07a2498255dfed51330610f1dd81e393195ba1 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 4 Feb 2024 21:15:54 -0500 Subject: [PATCH 054/221] =?UTF-8?q?Make=20the=20stub=20=E2=80=9Cmain?= =?UTF-8?q?=E2=80=9D=20instr=20explicitly=20invalid=20for=20clarity?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- alt/scheme/rvm.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index cc399fa..7b7f751 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -451,7 +451,8 @@ def emit_instr(op, arg, next): # emit_rib("main", "#0", start_instr, "#0", comment=f"jump {start_instr}") # Note: there is no true no-op, and "id" is not yet initialized. This will leave junk on the # stack. What we really want is to put the "main" label on start_instr when it's emitted. - emit_rib("main", "#3", "#-42", start_instr) + # Using an illegal opcode here just to ensure we never actually try to interpret it. + emit_rib("main", "#42", "#0", start_instr) BUILTINS = { From d1564e14f9a703bbea7eb20f857bdcdd9e26544b Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 4 Feb 2024 21:17:19 -0500 Subject: [PATCH 055/221] =?UTF-8?q?Handle=20=E2=80=9Chalt=E2=80=9D=20instr?= =?UTF-8?q?uctions=20in=20tracing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Still a weird bug in here somewhere… --- alt/scheme/rvm.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 7b7f751..8690f07 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -105,6 +105,8 @@ def show_target(val): return f"const {show_obj(y)} -> {show_addr(z)}" elif x == 4: return f"if -> {show_addr(y)} else {show_addr(z)}" + elif x == 5: + return "halt" else: return f"not an instr: {(x, y, z)}" @@ -124,10 +126,13 @@ def show_stack(addr): def go(addr): if addr == symbols["rib_nil"]: raise Exception("Unexpected nil in stack") + elif addr == 0: + # This appears only after the outer continuation is invoked: + return ["⊥"] elif peek(addr+2) == 0: # pair return go(peek(addr+1)) + [show_obj(peek(addr))] elif peek(addr) == 0 and peek(addr+1) == 0: - # This must be the primordial continuation rib: (0, 0, halt) + # This must be the primordial continuation rib: (0, 0, halt), before it is invoked # return ["⊥"] return [] else: From 672d50735e97ea389cfc0ec27a0e53724debe918 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 4 Feb 2024 21:28:11 -0500 Subject: [PATCH 056/221] =?UTF-8?q?Actually,=20honest-to-god=20complete=20?= =?UTF-8?q?execution=20of=20=E2=80=9C42=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- alt/scheme/rvm.py | 419 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 292 insertions(+), 127 deletions(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 8690f07..bc9432b 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -22,7 +22,7 @@ """Log every CPU instruction (in addition to COARSE and FINE logging.)""" -def run(program, print_asm=True, trace_level=TRACE_FINE, verbose_tty=True): +def run(program, print_asm=True, trace_level=TRACE_COARSE, verbose_tty=True): encoded = compile(program) # print(f"encoded program: {repr(encoded)}") @@ -125,16 +125,13 @@ def show_obj(val, deep=True): def show_stack(addr): def go(addr): if addr == symbols["rib_nil"]: - raise Exception("Unexpected nil in stack") - elif addr == 0: # This appears only after the outer continuation is invoked: - return ["⊥"] + return [] + elif addr == 0: + # sanity check + raise Exception("Unexpected zero pointer in stack") elif peek(addr+2) == 0: # pair return go(peek(addr+1)) + [show_obj(peek(addr))] - elif peek(addr) == 0 and peek(addr+1) == 0: - # This must be the primordial continuation rib: (0, 0, halt), before it is invoked - # return ["⊥"] - return [] else: # A continuation: (stack, closure, next instr) return go(peek(addr)) + [f"cont(@{peek(addr+2)})"] @@ -155,7 +152,7 @@ def show_addr(addr): print(f"{cycles:,d} (+{cycles - last_traced_exec:,d}):") last_traced_exec = cycles - print(f" stack: ({show_addr(peek(0))}) {show_stack(peek(0))}") + print(f" stack ({show_addr(peek(0))}): {show_stack(peek(0))}") next_rib = unsigned(peek(2)) current_ribs = (next_rib - big.HEAP_BASE)//3 @@ -331,7 +328,7 @@ def emit_vector(lbl, elems, count: int): emit_rib(lbl, elems, f"#{count}", "#4" asm.blank() - emit_rib("rib_outer_cont", "#0", "#0", "@instr_halt", "Bottom of stack: continuation to halt") + emit_rib("rib_outer_cont", "@rib_nil", "#0", "@instr_halt", "Bottom of stack: continuation to halt") # Decode RVM instructions: @@ -468,6 +465,8 @@ def emit_instr(op, arg, next): "PC": 1, "NEXT_RIB": 2, + "PRIMITIVE_CONT": 3, # where to go when primitive handler is done + "SYMBOL_TABLE": 4, # Only used during intiialization? # General-purpose temporary storage for the interpreter/primitives: @@ -481,8 +480,23 @@ def emit_instr(op, arg, next): "MAX_RIB": big.HEAP_TOP, # The largest value that can ever be the index of a slot, as opposed to the address of a global (symbol) + # TODO: adjust for encoded rib pointers "MAX_SLOT": big.ROM_BASE-1, } +"""Constants (mostly addresses) that are used in the generated assembly.""" + + +def tag_int(val): + """Encode an integer value so the runtime will interpret it as a signed integer.""" + return val + +def tag_rib_pointer(addr): + """Encode an address so the runtime will interpret it as a pointer to a rib.""" + assert addr < -big.ROM_BASE or addr >= big.ROM_BASE + return addr + +FIRST_RIB = BUILTINS["FIRST_RIB_MINUS_ONE"] + 1 +assert BUILTINS["MAX_SLOT"] < tag_rib_pointer(FIRST_RIB) def interpreter(asm): @@ -685,6 +699,35 @@ def z_to_d(rib_addr_loc): asm.instr("A=A+1") # Cheeky: add two ones instead of using @2 to save a cycle asm.instr("D=M") + def d_is_not_slot(): + """Test if the value in D is a slot index, leaving 0 in D if so.""" + asm.instr("@1023") + asm.instr("A=!A") + asm.instr("D=D&A") + + def find_cont(dest): + cont_loop_test = asm.next_label("cont_loop_test") + cont_loop_end = asm.next_label("cont_loop_end") + asm.comment("R5 = RAM[SP]") + asm.instr("@SP") + asm.instr("D=M") + asm.instr(f"@{dest}") + asm.instr("M=D") + asm.label(cont_loop_test) + z_to_d(dest) + asm.instr(f"@{cont_loop_end}") + asm.instr("D;JNE") + + asm.comment("R5 = R5.y") + y_to_d(dest) + asm.instr(f"@{dest}") + asm.instr("M=D") + asm.instr(f"@{cont_loop_test}") + asm.instr("0;JMP") + + asm.label(cont_loop_end) + + asm.comment("First time: start with the 'next' of main (by falling through to continue_next)") asm.comment("Typical loop path: get the next instruction to interpret from the third field of the current instruction:") @@ -732,69 +775,100 @@ def z_to_d(rib_addr_loc): asm.label("opcode_0") asm.comment("type 0: jump/call") - z_to_d("PC") - asm.instr("@handle_call") - asm.instr("D;JNE") - y_to_d("PC") - asm.instr(f"@MAX_SLOT") - asm.instr("D=D-A") - asm.instr("@handle_jump_to_slot") - asm.instr("D;JLT") - - asm.comment("TODO: TEMP_? = target proc, ...") - asm.instr("@halt_loop") - # asm.instr("0;JMP") - # asm.comment("HACK: PC = PC.y; bogus!") - # y_to_d("PC") - # asm.instr("@PC") - # asm.instr("M=D") - # asm.instr("@exec_loop") - # asm.instr("0;JMP") - asm.blank() - asm.label("handle_jump_to_slot") - asm.comment("TODO") - asm.instr("@halt_loop") - asm.instr("0;JMP") - asm.blank() - - asm.label("handle_call") + asm.comment("TEMP_3 = address of the proc rib") y_to_d("PC") - asm.instr(f"@MAX_SLOT") - asm.instr("D=D-A") - asm.instr("@handle_call_to_slot") - asm.instr("D;JLT") + d_is_not_slot() + asm.instr("@proc_from_slot") + asm.instr("D;JEQ") + asm.label("proc_from_global") asm.comment("TEMP_3 = proc rib from symbol") y_to_d("PC") - asm.instr("A=D") # HACK + asm.instr("A=D") # HACK: just load it to A from the instr? asm.instr("D=M") asm.instr("@TEMP_3") asm.instr("M=D") - asm.instr("@handle_call_start") + asm.instr("@handle_proc_start") asm.instr("0;JMP") asm.blank() - asm.label("handle_call_to_slot") + asm.label("proc_from_slot") asm.comment("TEMP_3 = proc rib from stack") asm.comment("TODO") asm.instr("@halt_loop") asm.instr("0;JMP") asm.blank() - asm.comment("call protocol:") + asm.label("handle_proc_start") + asm.instr("D=D") # no-op to make the label tracable - asm.label("handle_call_start") + # TODO: if type_checking: + asm.label("check_proc_rib") + z_to_d("TEMP_3") + asm.instr("D=D-1") + asm.instr("@halt_loop") + asm.instr("D;JNE") + asm.blank() + + asm.comment("Now if next is 0 -> jump; otherwise -> call") + z_to_d("PC") + asm.instr("@handle_call") + asm.instr("D;JNE") + + asm.label("handle_jump") - asm.comment("Check is proc rib:") + asm.comment("Check primitive or closure:") asm.instr("@TEMP_3") asm.instr("A=M") - asm.instr("A=A+1") - asm.instr("A=A+1") - asm.instr("D=M-1") - asm.instr("@halt_loop") + asm.instr("D=M") + asm.instr("@0x1F") # Mask off bits that are zero iff it's a primitive + asm.instr("A=!A") + asm.instr("D=D&A") + asm.instr("@handle_jump_to_closure") asm.instr("D;JNE") +# when a primitive is called through a jump instruction, ... before the result is pushed to +# the stack the RVM’s stack and pc variables are updated according to the continuation in +# the current stack frame which contains the state of those variables when the call was +# executed (the details are given in Section 2.7). + asm.label("handle_jump_to_primitive") + asm.comment("set target to continue after handling the op") + asm.instr("@after_primitive_for_jump") + asm.instr("D=A") + asm.instr("@PRIMITIVE_CONT") + asm.instr("M=D") + asm.instr("@handle_primitive") + asm.instr("0;JMP") + + asm.label("after_primitive_for_jump") + + asm.comment("find the continuation rib: first rib on stack with non-zero third field") + + find_cont("TEMP_0") + + asm.comment("overwrite the top stack entry: SP.y = R5.x") + x_to_d("TEMP_0") + asm.instr("@SP") + asm.instr("A=M+1") + asm.instr("M=D") + + asm.comment("PC = R5.z") + z_to_d("TEMP_0") + asm.instr("@PC") + asm.instr("M=D") + asm.instr("@exec_loop") + asm.instr("0;JMP") + asm.blank() + + asm.label("handle_jump_to_closure") + asm.instr("@halt_loop") + asm.instr("0;JMP") + asm.blank() + + # "next" is not 0, so this is a call + asm.label("handle_call") + asm.comment("Check primitive or closure:") asm.instr("@TEMP_3") asm.instr("A=M") @@ -806,14 +880,13 @@ def z_to_d(rib_addr_loc): asm.instr("D;JNE") asm.label("handle_call_primitive") - asm.instr("@TEMP_3") - asm.instr("A=M") - asm.instr("D=M") - asm.instr("@primitive_vector_table_start") - asm.instr("A=A+D") - asm.instr("A=M") + asm.comment("set target to continue after handling the op") + asm.instr("@continue_next") + asm.instr("D=A") + asm.instr("@PRIMITIVE_CONT") + asm.instr("M=D") + asm.instr("@handle_primitive") asm.instr("0;JMP") - asm.blank() asm.label("handle_call_closure") asm.comment("R5 = new rib for the continuation") @@ -865,10 +938,9 @@ def z_to_d(rib_addr_loc): asm.label("opcode_1") asm.comment("type 1: set") y_to_d("PC") - asm.instr(f"@MAX_SLOT") - asm.instr("D=D-A") + d_is_not_slot() asm.instr("@handle_set_slot") - asm.instr("D;JLT") + asm.instr("D;JEQ") asm.label("handle_set_global") asm.comment("R5 = address of symbol rib") @@ -941,7 +1013,6 @@ def z_to_d(rib_addr_loc): asm.instr("0;JMP") asm.blank() - # # Halt: # @@ -953,9 +1024,75 @@ def z_to_d(rib_addr_loc): # - # Primitive handlers: + # Primitive handling: # + asm.label("handle_primitive") + asm.instr("@TEMP_3") + asm.instr("A=M") + asm.instr("D=M") + asm.instr("@primitive_vector_table_start") + asm.instr("A=A+D") + asm.instr("A=M") + asm.instr("0;JMP") + asm.blank() + + asm.comment("=== Primitive vectors ===") + asm.label("primitive_vector_table_start") + asm.comment("0") + asm.instr("@primitive_rib") + asm.comment("1") + asm.instr("@primitive_id") + asm.comment("2") + asm.instr("@primitive_arg1") + asm.comment("3") + asm.instr("@primitive_arg2") + asm.comment("4") + asm.instr("@primitive_close") + asm.comment("5") + asm.instr("@primitive_rib?") + asm.comment("6") + asm.instr("@primitive_field0") + asm.comment("7") + asm.instr("@primitive_field1") + asm.comment("8") + asm.instr("@primitive_field2") + asm.comment("9") + asm.instr("@primitive_field0-set!") + asm.comment("10") + asm.instr("@primitive_field1-set!") + asm.comment("11") + asm.instr("@primitive_field2-set!") + asm.comment("12") + asm.instr("@primitive_eqv?") + asm.comment("13") + asm.instr("@primitive_<") + asm.comment("14") + asm.instr("@primitive_+") + asm.comment("15") + asm.instr("@primitive_-") + asm.comment("16") + asm.instr("@primitive_*") + asm.comment("17 (quotient: not implemented)") + asm.instr("@primitive_unimp") + asm.comment("18 (getchar: not implemented)") + asm.instr("@primitive_unimp") + asm.comment("19 (putchar: not implemented)") + asm.instr("@primitive_unimp") + asm.comment("20") + asm.instr("@primitive_peek") + asm.comment("21") + asm.instr("@primitive_poke") + asm.comment("22") + asm.instr("@primitive_halt") + # fatal? + asm.comment("dummy handlers for 23-31 to simplify range check above:") + for op in range(23, 32): + asm.comment(f"{op} (dummy)") + asm.label("primitive_vectors_end") + + asm.blank() + asm.label("primitive_rib") asm.comment("primitive 0; rib :: x y z -- rib(x, y, z)") pop("TEMP_2") @@ -989,117 +1126,145 @@ def z_to_d(rib_addr_loc): asm.instr("@SP") asm.instr("M=D") - asm.instr("@continue_next") + asm.instr("@PRIMITIVE_CONT") + asm.instr("A=M") asm.instr("0;JMP") asm.blank() - asm.comment("TODO: for now, all these just fall through to halt/unimp") asm.label("primitive_id") asm.comment("primitive 1; id :: x -- x") + asm.instr("@PRIMITIVE_CONT") + asm.instr("A=M") + asm.instr("0;JMP") + asm.blank() + asm.label("primitive_arg1") asm.comment("primitive 2; arg1 :: x y -- x") + asm.comment("TODO") + asm.instr("@halt_loop") + asm.instr("0;JMP") + asm.blank() + asm.label("primitive_arg2") asm.comment("primitive 3; arg2 :: x y -- y") + asm.comment("TODO") + asm.instr("@halt_loop") + asm.instr("0;JMP") + asm.blank() + asm.label("primitive_close") asm.comment("primitive 4; close :: x -- rib(x[0], stack, 1)") + asm.comment("TODO") + asm.instr("@halt_loop") + asm.instr("0;JMP") + asm.blank() + asm.label("primitive_rib?") asm.comment("primitive 5; rib? :: x -- bool(x is a rib)") + asm.comment("TODO") + asm.instr("@halt_loop") + asm.instr("0;JMP") + asm.blank() + asm.label("primitive_field0") asm.comment("primitive 6; field0 :: rib(x, _, _) -- x") + asm.comment("TODO") + asm.instr("@halt_loop") + asm.instr("0;JMP") + asm.blank() + asm.label("primitive_field1") asm.comment("primitive 7; field1 :: rib(_, y, _) -- y") + asm.comment("TODO") + asm.instr("@halt_loop") + asm.instr("0;JMP") + asm.blank() + asm.label("primitive_field2") asm.comment("primitive 8; field2 :: rib(_, _, z) -- z") + asm.comment("TODO") + asm.instr("@halt_loop") + asm.instr("0;JMP") + asm.blank() + asm.label("primitive_field0-set!") asm.comment("primitive 9; field0-set! :: rib(_, y, z) x -- x (and update the rib in place: rib(x, y, z))") + asm.comment("TODO") + asm.instr("@halt_loop") + asm.instr("0;JMP") + asm.blank() + asm.label("primitive_field1-set!") asm.comment("primitive 10; field1-set! :: rib(x, _, z) y -- y (and update the rib in place: rib(x, y, z))") + asm.comment("TODO") + asm.instr("@halt_loop") + asm.instr("0;JMP") + asm.blank() + asm.label("primitive_field2-set!") asm.comment("primitive 11; field2-set! :: rib(x, y, _) z -- z (and update the rib in place: rib(x, y, z))") + asm.comment("TODO") + asm.instr("@halt_loop") + asm.instr("0;JMP") + asm.blank() + asm.label("primitive_eqv?") asm.comment("primitive 12; eqv? :: x y -- bool(x is identical to y)") + asm.comment("TODO") + asm.instr("@halt_loop") + asm.instr("0;JMP") + asm.blank() + asm.label("primitive_<") asm.comment("primitive 13; < :: x y -- bool(x < y)") + asm.comment("TODO") + asm.instr("@halt_loop") + asm.instr("0;JMP") + asm.blank() + asm.label("primitive_+") asm.comment("primitive 14; + :: x y -- x + y") + asm.comment("TODO") + asm.instr("@halt_loop") + asm.instr("0;JMP") + asm.blank() + asm.label("primitive_-") asm.comment("primitive 15; - :: x y -- x - y") + asm.comment("TODO") + asm.instr("@halt_loop") + asm.instr("0;JMP") + asm.blank() + asm.label("primitive_*") asm.comment("primitive 16; - :: x y -- x * y") + asm.comment("TODO") + asm.instr("@halt_loop") + asm.instr("0;JMP") + asm.blank() + asm.label("primitive_peek") asm.comment("primitive 19; peek :: x -- RAM[x]") + asm.comment("TODO") + asm.instr("@halt_loop") + asm.instr("0;JMP") + asm.blank() + asm.label("primitive_poke") asm.comment("primitive 20; poke :: x y -- y (and write the value y at RAM[x])") + asm.comment("TODO") + asm.instr("@halt_loop") + asm.instr("0;JMP") + asm.blank() asm.label("primitive_halt") asm.comment("primitive 21; halt :: -- (no more instructions are executed)") asm.instr("@halt_loop") asm.instr("0;JMP") + asm.blank() asm.label("primitive_unimp") asm.comment("Note: the current instr will be logged if tracing is enabled") asm.instr("@halt_loop") asm.instr("0;JMP") - - asm.comment("=== Primitive vectors ===") - asm.label("primitive_vector_table_start") - asm.comment("0") - asm.instr("@primitive_rib") - asm.comment("1") - asm.instr("@primitive_id") - asm.comment("2") - asm.instr("@primitive_arg1") - asm.comment("3") - asm.instr("@primitive_arg2") - asm.comment("4") - asm.instr("@primitive_close") - asm.comment("5") - asm.instr("@primitive_rib?") - asm.comment("6") - asm.instr("@primitive_field0") - asm.comment("7") - asm.instr("@primitive_field1") - asm.comment("8") - asm.instr("@primitive_field2") - asm.comment("9") - asm.instr("@primitive_field0-set!") - asm.comment("10") - asm.instr("@primitive_field1-set!") - asm.comment("11") - asm.instr("@primitive_field2-set!") - asm.comment("12") - asm.instr("@primitive_eqv?") - asm.comment("13") - asm.instr("@primitive_<") - asm.comment("14") - asm.instr("@primitive_+") - asm.comment("15") - asm.instr("@primitive_-") - asm.comment("16") - asm.instr("@primitive_*") - asm.comment("17 (quotient: not implemented)") - asm.instr("@primitive_unimp") - asm.comment("18 (getchar: not implemented)") - asm.instr("@primitive_unimp") - asm.comment("19 (putchar: not implemented)") - asm.instr("@primitive_unimp") - asm.comment("20") - asm.instr("@primitive_peek") - asm.comment("21") - asm.instr("@primitive_poke") - asm.comment("22") - asm.instr("@primitive_halt") - # fatal? - asm.comment("dummy handlers for 23-31 to simplify range check above:") - asm.instr("@primitive_unimp") - asm.instr("@primitive_unimp") - asm.instr("@primitive_unimp") - asm.instr("@primitive_unimp") - asm.instr("@primitive_unimp") - asm.instr("@primitive_unimp") - asm.instr("@primitive_unimp") - asm.instr("@primitive_unimp") - asm.instr("@primitive_unimp") - asm.label("primitive_vectors_end") - - asm.blank() \ No newline at end of file + asm.blank() From ff61aa6306ee1e6d97ffcdf5bb0d6da13cd28505 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 4 Feb 2024 21:36:09 -0500 Subject: [PATCH 057/221] Unit test the for one Scheme program that will currently run --- alt/scheme/test_rvm.py | 96 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100755 alt/scheme/test_rvm.py diff --git a/alt/scheme/test_rvm.py b/alt/scheme/test_rvm.py new file mode 100755 index 0000000..6d9a6ef --- /dev/null +++ b/alt/scheme/test_rvm.py @@ -0,0 +1,96 @@ +#! /usr/bin/env pytest + +import pytest + +from alt import big +from alt.scheme import rvm +import nand +from nand.translate import AssemblySource +from nand.vector import unsigned + +def test_trivial(): + program = "42" + + peek, output = run_to_halt(program, max_cycles=20000) + + assert get_stack(peek) == [42] + assert output == [] + + + +def get_stack(peek): + def loop(addr): + car = peek(addr) + cdr = peek(addr+1) + type_ = peek(addr+2) + if type_ == 5: + # It's a "special", so assume it's the nil value + return [] + elif type_ == 0: + return loop(cdr) + [car] + else: + # must be a continuation rib: + return loop(car) + [f"cont@{addr}"] + + return loop(peek(0)) + + +def run_to_halt(program, max_cycles=5000): + """Compile and run a Scheme program, then return a function for inspecting the RAM, and a + list of words that were written to the TTY port. + """ + + encoded = rvm.compile(program) + # print(f"encoded program: {repr(encoded)}") + + asm = AssemblySource() + + rvm.interpreter(asm) + + rvm.decode(encoded, asm) + + instrs, symbols, _ = big.assemble(asm.lines, min_static=None, builtins=rvm.BUILTINS) + + symbols_by_addr = { addr: name for (name, addr) in symbols.items() } + + for addr, name in symbols_by_addr.items(): + print(f"{addr}: {name}") + + def show_addr(addr): + # addr = unsigned + if addr in symbols_by_addr: + return f"{symbols_by_addr[addr]} (@{addr})" + else: + return f"@{addr}" + + + computer = nand.syntax.run(big.BigComputer, simulator="vector") + + computer.init_rom(instrs) + + # Jump over low memory that we might be using for debugging: + computer.poke(0, big.ROM_BASE) + computer.poke(1, big.parse_op("0;JMP")) + + cycles = 0 + output = [] + + while (not computer.fetch or computer.pc != symbols["halt_loop"]) and cycles <= max_cycles: + computer.ticktock() + cycles += 1 + + tty_char = computer.get_tty() + if tty_char: + output.append(tty_char) + + if computer.fetch: + print(f"pc: {show_addr(computer.pc)}; PC: {show_addr(computer.peek(1))}") + + def peek(addr): + """Read from RAM or ROM.""" + if big.ROM_BASE <= addr < big.HEAP_BASE: + return computer._rom.storage[addr] + else: + return computer.peek(addr) + + return (peek, output) From 1e8cfeb45efc512327f3617373668e6c261280f6 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 4 Feb 2024 21:46:46 -0500 Subject: [PATCH 058/221] =?UTF-8?q?Implement=20=E2=80=9C+=E2=80=9D,=20and?= =?UTF-8?q?=20test=20it?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- alt/scheme/rvm.py | 14 ++++++++++++-- alt/scheme/test_rvm.py | 9 +++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index bc9432b..325caca 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -1133,6 +1133,7 @@ def find_cont(dest): asm.label("primitive_id") asm.comment("primitive 1; id :: x -- x") + asm.comment("... and, that's all folks") asm.instr("@PRIMITIVE_CONT") asm.instr("A=M") asm.instr("0;JMP") @@ -1224,8 +1225,17 @@ def find_cont(dest): asm.label("primitive_+") asm.comment("primitive 14; + :: x y -- x + y") - asm.comment("TODO") - asm.instr("@halt_loop") + # TODO: if type_checking: + asm.comment("D = pop() = y") + pop("TEMP_0") + asm.instr("@TEMP_0") + asm.instr("D=M") + asm.comment("SP.x += y") + asm.instr("@SP") + asm.instr("A=M") + asm.instr("M=M+D") # just update SP.x in place, + asm.instr("@PRIMITIVE_CONT") + asm.instr("A=M") asm.instr("0;JMP") asm.blank() diff --git a/alt/scheme/test_rvm.py b/alt/scheme/test_rvm.py index 6d9a6ef..adceb93 100755 --- a/alt/scheme/test_rvm.py +++ b/alt/scheme/test_rvm.py @@ -17,6 +17,15 @@ def test_trivial(): assert output == [] +def test_add(): + program = "(+ 1 2)" + + peek, output = run_to_halt(program, max_cycles=20000) + + assert get_stack(peek) == [3] + assert output == [] + + def get_stack(peek): def loop(addr): From 86aa302ec1e1af3c49cd8aa73c5e012fb58cbfc7 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 4 Feb 2024 21:50:17 -0500 Subject: [PATCH 059/221] =?UTF-8?q?Implement=20=E2=80=9Csub=E2=80=9D,=20wi?= =?UTF-8?q?th=20a=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- alt/scheme/rvm.py | 19 ++++++++++++++----- alt/scheme/test_rvm.py | 9 +++++++++ 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 325caca..74759aa 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -22,14 +22,14 @@ """Log every CPU instruction (in addition to COARSE and FINE logging.)""" -def run(program, print_asm=True, trace_level=TRACE_COARSE, verbose_tty=True): +def run(program, print_asm=True, trace_level=TRACE_FINE, verbose_tty=True): encoded = compile(program) # print(f"encoded program: {repr(encoded)}") run_compiled(encoded, print_asm, trace_level, verbose_tty) -def run_compiled(encoded, print_asm=True, trace_level=TRACE_FINE, verbose_tty=True): +def run_compiled(encoded, print_asm=True, trace_level=TRACE_COARSE, verbose_tty=True): asm = AssemblySource() @@ -1233,7 +1233,7 @@ def find_cont(dest): asm.comment("SP.x += y") asm.instr("@SP") asm.instr("A=M") - asm.instr("M=M+D") # just update SP.x in place, + asm.instr("M=M+D") asm.instr("@PRIMITIVE_CONT") asm.instr("A=M") asm.instr("0;JMP") @@ -1241,8 +1241,17 @@ def find_cont(dest): asm.label("primitive_-") asm.comment("primitive 15; - :: x y -- x - y") - asm.comment("TODO") - asm.instr("@halt_loop") + # TODO: if type_checking: + asm.comment("D = pop() = y") + pop("TEMP_0") + asm.instr("@TEMP_0") + asm.instr("D=M") + asm.comment("SP.x -= y") + asm.instr("@SP") + asm.instr("A=M") + asm.instr("M=M-D") + asm.instr("@PRIMITIVE_CONT") + asm.instr("A=M") asm.instr("0;JMP") asm.blank() diff --git a/alt/scheme/test_rvm.py b/alt/scheme/test_rvm.py index adceb93..4118e20 100755 --- a/alt/scheme/test_rvm.py +++ b/alt/scheme/test_rvm.py @@ -26,6 +26,15 @@ def test_add(): assert output == [] +def test_sub(): + program = "(- 123 234)" + + peek, output = run_to_halt(program, max_cycles=20000) + + assert get_stack(peek) == [-111] + assert output == [] + + def get_stack(peek): def loop(addr): From dd209ad23032e5bd82c2fa8103db2552b097c640 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Mon, 5 Feb 2024 20:31:17 -0500 Subject: [PATCH 060/221] Implement get (global) --- alt/scheme/rvm.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 74759aa..08b837f 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -966,6 +966,34 @@ def find_cont(dest): asm.label("opcode_2") asm.comment("type 2: get") + y_to_d("PC") + d_is_not_slot() + asm.instr("@handle_get_slot") + asm.instr("D;JEQ") + + asm.label("handle_get_global") + asm.instr("@NEXT_RIB") + asm.instr("D=M") + asm.instr("@TEMP_0") + asm.instr("M=D") + y_to_d("PC") # D = address of symbol + asm.instr("A=D") # TODO: load directly to A? + asm.instr("D=M") # D = object at symbol.x + rib_append("D") + asm.instr("@SP") + asm.instr("D=M") + rib_append("D") + rib_append("0") # pair + asm.instr("@TEMP_0") + asm.instr("D=M") + asm.instr("@SP") + asm.instr("M=D") + + asm.instr("@continue_next") + asm.instr("0;JMP") + asm.blank() + + asm.label("handle_get_slot") asm.comment("TODO") asm.instr("@halt_loop") asm.instr("0;JMP") From 647266db4d5c60768caf73ed6546623e49309509 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Mon, 5 Feb 2024 20:32:37 -0500 Subject: [PATCH 061/221] Halt on call to non-primitive (not implemented yet) --- alt/scheme/rvm.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 08b837f..db9cccc 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -931,7 +931,9 @@ def find_cont(dest): asm.comment("- PC = PC.y.z (proc entry point)") - asm.instr("@continue_next") + # TODO + # asm.instr("@continue_next") + asm.instr("@halt_loop") asm.instr("0;JMP") asm.blank() From 8bc9c69bd24e96effd13aa4255922526c0dd80f5 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Mon, 5 Feb 2024 20:37:09 -0500 Subject: [PATCH 062/221] Tracing: actually decode ribs on the stack helpfully --- alt/scheme/rvm.py | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index db9cccc..c7b30c5 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -22,7 +22,7 @@ """Log every CPU instruction (in addition to COARSE and FINE logging.)""" -def run(program, print_asm=True, trace_level=TRACE_FINE, verbose_tty=True): +def run(program, print_asm=True, trace_level=TRACE_COARSE, verbose_tty=True): encoded = compile(program) # print(f"encoded program: {repr(encoded)}") @@ -114,13 +114,37 @@ def show_obj(val, deep=True): # FIXME: check tag if -big.ROM_BASE < extend_sign(val) < big.ROM_BASE: return f"{extend_sign(val)}" + elif val == symbols["rib_nil"]: + return "nil" + elif val == symbols["rib_true"]: + return "#t" + elif val == symbols["rib_false"]: + return "#f" else: x, y, z = peek(val), peek(val+1), peek(val+2) - if deep: - return f"({show_obj(x, False)}, {show_obj(y, False)}, {show_obj(z, False)})" + if z == 0: # pair + return f"({show_obj(x)} : {show_obj(y)}" + elif z == 1: # proc + MAX_PRIMITIVE = 31 + if 0 < x < MAX_PRIMITIVE: + return f"primitive(#{x})" # TODO: lookup the primitive's name + else: + num_args, instr = peek(x), peek(x+2) + return f"proc(args={num_args}, env={show_obj(y)}, instr={show_addr(instr)}){show_addr(val)}" + # elif z == 2: # symbol + # elif z == 3: # string + # elif z == 4: # vector + elif z == 5: + # Unexpected, but show the contents just in case + return f"special({show_obj(x)}, {show_obj(y)}){show_addr(val)}" else: - # return f"{x, y, z}" - return f"(@{unsigned(x)}, @{unsigned(y)}, @{unsigned(z)})" + return f"TODO: ({x}, {y}, {z})" + + # elif deep: + # return f"({show_obj(x, False)}, {show_obj(y, False)}, {show_obj(z, False)})" + # else: + # # return f"{x, y, z}" + # return f"(@{unsigned(x)}, @{unsigned(y)}, @{unsigned(z)})" def show_stack(addr): def go(addr): @@ -318,6 +342,7 @@ def emit_vector(lbl, elems, count: int): emit_rib(lbl, elems, f"#{count}", "#4" # TODO: move this table elsewhere, so the ribs for strings and instructions form a monolithic # block of address space? + # TODO: add the initial value for each symbol to this table asm.comment("Table of pointers to symbol name ribs in ROM:") asm.label("symbol_names_start") for lbl, s in reversed(sym_names): @@ -330,6 +355,8 @@ def emit_vector(lbl, elems, count: int): emit_rib(lbl, elems, f"#{count}", "#4" emit_rib("rib_outer_cont", "@rib_nil", "#0", "@instr_halt", "Bottom of stack: continuation to halt") + asm.blank() + # Decode RVM instructions: asm.comment("Instructions:") From b9c974a55f47fabf69e2771ca2d00ccf7f71f117 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Tue, 6 Feb 2024 10:56:48 -0500 Subject: [PATCH 063/221] Factor out memory inspection --- alt/scheme/inspector.py | 110 ++++++++++++++++++++++++++++++++++++++++ alt/scheme/rvm.py | 107 ++++---------------------------------- 2 files changed, 119 insertions(+), 98 deletions(-) create mode 100644 alt/scheme/inspector.py diff --git a/alt/scheme/inspector.py b/alt/scheme/inspector.py new file mode 100644 index 0000000..78d40ac --- /dev/null +++ b/alt/scheme/inspector.py @@ -0,0 +1,110 @@ +"""Python wrappers for Scheme types, for tracing purposes.""" + +from nand.vector import extend_sign, unsigned +from alt import big + + +class Inspector: + def __init__(self, computer, symbols={}, rom_base=big.ROM_BASE, rom_limit=big.HEAP_BASE): + def peek(addr): + if rom_base <= addr < rom_limit: + return computer.peek_rom(addr) + else: + return computer.peek(addr) + + self.peek = peek + self.symbols_by_addr = { addr: name for (name, addr) in symbols.items() } + + + def show_addr(self, addr): + """Show an address (with "@"), using the symbol for addresses in ROM.""" + if addr in self.symbols_by_addr: + return f"@{self.symbols_by_addr[addr]}" + else: + return f"@{unsigned(addr)}" + + + def show_instr(self, addr): + x, y, z = self.peek(addr), self.peek(addr+1), self.peek(addr+2) + + def show_target(): + """The target of a jump/call, get, or set: either a slot index or global.""" + + # FIXME: use the rvm's actual/current value + MAX_SLOT = big.ROM_BASE-1 + if 0 <= y <= MAX_SLOT: + return f"#{y}" + else: + return self.show_addr(y) + + if x == 0 and z == 0: + return f"jump {show_target()}" + elif x == 0: + return f"call {show_target()}" # -> {self.show_addr(z)}" + elif x == 1: + return f"set {show_target()}" # -> {self.show_addr(z)}" + elif x == 2: + return f"get {show_target()}" # -> {self.show_addr(z)}" + elif x == 3: + return f"const {self.show_obj(y)}" # -> {self.show_addr(z)}" + elif x == 4: + return f"if -> {self.show_addr(y)} else {self.show_addr(z)}" + elif x == 5: + return "halt" + else: + return f"not an instr: {(x, y, z)}" + + + def show_obj(self, val): + """Show an object, which may be an integer, special value, or rib.""" + + # FIXME: check tag + if -big.ROM_BASE < extend_sign(val) < big.ROM_BASE: + return f"{extend_sign(val)}" + elif self.symbols_by_addr.get(val) == "rib_nil": + return "nil" + elif self.symbols_by_addr.get(val) == "rib_true": + return "#t" + elif self.symbols_by_addr.get(val) == "rib_false": + return "#f" + else: + x, y, z = self.peek(val), self.peek(val+1), self.peek(val+2) + if z == 0: # pair + return f"({self.show_obj(x)} : {self.show_obj(y)}" + elif z == 1: # proc + MAX_PRIMITIVE = 31 + if 0 < x < MAX_PRIMITIVE: + return f"primitive(#{x})" # TODO: lookup the primitive's name + else: + num_args, instr = self.peek(x), self.peek(x+2) + return f"proc(args={num_args}, env={self.show_obj(y)}, instr={self.show_addr(instr)}){self.show_addr(val)}" + # elif z == 2: # symbol + # elif z == 3: # string + # elif z == 4: # vector + elif z == 5: + # Unexpected, but show the contents just in case + return f"special({self.show_obj(x)}, {self.show_obj(y)}){self.show_addr(val)}" + else: + return f"TODO: ({x}, {y}, {z})" + + def show_stack(self): + """Show the contents of the stack, which is a list composed of ordinary pairs and continuation + ribs.""" + + def go(addr): + if self.symbols_by_addr.get(addr) == "rib_nil": + # This appears only after the outer continuation is invoked: + return [] + elif addr == 0: + # sanity check + raise Exception("Unexpected zero pointer in stack") + else: + x, y, z = self.peek(addr), self.peek(addr+1), self.peek(addr+2) + + if z == 0: # pair + return go(y) + [self.show_obj(x)] + else: + # A continuation: (stack, closure, next instr) + return go(x) + [f"cont({self.show_addr(z)})"] + SP = 0 + return ", ".join(go(self.peek(SP))) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index c7b30c5..e855a0d 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -5,11 +5,10 @@ See http://www.iro.umontreal.ca/~feeley/papers/YvonFeeleyVMIL21.pdf for the general picture. """ -import computer -# import nand from nand.translate import AssemblySource from alt import big from nand.vector import extend_sign, unsigned +from alt.scheme.inspector import Inspector TRACE_NONE = 0 @@ -78,95 +77,7 @@ def show_map(label, m): def trace(computer, cycles): nonlocal last_traced_exec - def peek(addr): - """Read from RAM or ROM.""" - if big.ROM_BASE <= addr < big.HEAP_BASE: - return computer._rom.storage[addr] - else: - return computer.peek(addr) - - def show_instr(addr): - def show_target(val): - if val <= BUILTINS["MAX_SLOT"]: - return f"#{val}" - else: - return show_addr(val) - - x, y, z = peek(addr), peek(addr+1), peek(addr+2) - if x == 0 and z == 0: - return f"jump {show_target(y)}" - elif x == 0: - return f"call {show_target(y)} -> {show_addr(z)}" - elif x == 1: - return f"set {show_target(y)} -> {show_addr(z)}" - elif x == 2: - return f"get {show_target(y)} -> {show_addr(z)}" - elif x == 3: - return f"const {show_obj(y)} -> {show_addr(z)}" - elif x == 4: - return f"if -> {show_addr(y)} else {show_addr(z)}" - elif x == 5: - return "halt" - else: - return f"not an instr: {(x, y, z)}" - - def show_obj(val, deep=True): - # FIXME: check tag - if -big.ROM_BASE < extend_sign(val) < big.ROM_BASE: - return f"{extend_sign(val)}" - elif val == symbols["rib_nil"]: - return "nil" - elif val == symbols["rib_true"]: - return "#t" - elif val == symbols["rib_false"]: - return "#f" - else: - x, y, z = peek(val), peek(val+1), peek(val+2) - if z == 0: # pair - return f"({show_obj(x)} : {show_obj(y)}" - elif z == 1: # proc - MAX_PRIMITIVE = 31 - if 0 < x < MAX_PRIMITIVE: - return f"primitive(#{x})" # TODO: lookup the primitive's name - else: - num_args, instr = peek(x), peek(x+2) - return f"proc(args={num_args}, env={show_obj(y)}, instr={show_addr(instr)}){show_addr(val)}" - # elif z == 2: # symbol - # elif z == 3: # string - # elif z == 4: # vector - elif z == 5: - # Unexpected, but show the contents just in case - return f"special({show_obj(x)}, {show_obj(y)}){show_addr(val)}" - else: - return f"TODO: ({x}, {y}, {z})" - - # elif deep: - # return f"({show_obj(x, False)}, {show_obj(y, False)}, {show_obj(z, False)})" - # else: - # # return f"{x, y, z}" - # return f"(@{unsigned(x)}, @{unsigned(y)}, @{unsigned(z)})" - - def show_stack(addr): - def go(addr): - if addr == symbols["rib_nil"]: - # This appears only after the outer continuation is invoked: - return [] - elif addr == 0: - # sanity check - raise Exception("Unexpected zero pointer in stack") - elif peek(addr+2) == 0: # pair - return go(peek(addr+1)) + [show_obj(peek(addr))] - else: - # A continuation: (stack, closure, next instr) - return go(peek(addr)) + [f"cont(@{peek(addr+2)})"] - return ", ".join(go(addr)) - - def show_addr(addr): - """Show an address (with "@"), using the symbol for addresses in ROM.""" - if addr in symbols_by_addr: - return f"@{symbols_by_addr[addr]}" - else: - return f"@{unsigned(addr)}" + inspector = Inspector(computer, symbols) if (trace_level >= TRACE_COARSE and (computer.pc == symbols["exec_loop"] or computer.pc == symbols["halt_loop"])): @@ -176,21 +87,21 @@ def show_addr(addr): print(f"{cycles:,d} (+{cycles - last_traced_exec:,d}):") last_traced_exec = cycles - print(f" stack ({show_addr(peek(0))}): {show_stack(peek(0))}") + print(f" stack ({inspector.show_addr(inspector.peek(0))}): {inspector.show_stack()}") - next_rib = unsigned(peek(2)) + next_rib = unsigned(inspector.peek(2)) current_ribs = (next_rib - big.HEAP_BASE)//3 max_ribs = (big.HEAP_TOP - big.HEAP_BASE)//3 print(f" heap: {current_ribs:3,d} ({100*current_ribs/max_ribs:0.1f}%)") - print(f" PC: {show_addr(peek(1))}") + print(f" PC: {inspector.show_addr(inspector.peek(1))}") # # HACK? - # print(f" symbols (n..0): ({show_addr(peek(4))}) {show_stack(peek(4))}") + # print(f" symbols (n..0): ({inspector.show_addr(inspector.peek(4))}) {inspector.show_stack(inspector.peek(4))}") # print(f" ribs:") - # for addr in range(big.HEAP_BASE, unsigned(computer.peek(BUILTINS["NEXT_RIB"])), 3): - # print(f" @{addr}; {show_obj(addr, deep=False)}") + # for addr in range(big.HEAP_BASE, unsigned(inspector.peek(BUILTINS["NEXT_RIB"])), 3): + # print(f" @{addr}; {inspector.show_obj(addr, deep=False)}") - print(f" {show_instr(peek(1))}") + print(f" {inspector.show_instr(inspector.peek(BUILTINS['PC']))}") elif trace_level >= TRACE_FINE and computer.pc in symbols_by_addr and symbols_by_addr[computer.pc] != "halt_loop": print(f"{cycles:3,d}: ({symbols_by_addr[computer.pc]})") elif trace_level >= TRACE_ALL: From 51b7f87b8ada2bc0caf56e1b8853a518188d4afa Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Tue, 6 Feb 2024 11:10:57 -0500 Subject: [PATCH 064/221] Use inspector in the tests --- alt/scheme/inspector.py | 38 ++++++++++++++++++++++++++------------ alt/scheme/test_rvm.py | 41 ++++++++++++++--------------------------- nand/vector.py | 8 ++++++++ 3 files changed, 48 insertions(+), 39 deletions(-) diff --git a/alt/scheme/inspector.py b/alt/scheme/inspector.py index 78d40ac..2492543 100644 --- a/alt/scheme/inspector.py +++ b/alt/scheme/inspector.py @@ -55,22 +55,22 @@ def show_target(): return f"not an instr: {(x, y, z)}" - def show_obj(self, val): - """Show an object, which may be an integer, special value, or rib.""" + def _obj(self, val): + """Python representation of an object, which may be an integer, special value, or rib.""" # FIXME: check tag if -big.ROM_BASE < extend_sign(val) < big.ROM_BASE: - return f"{extend_sign(val)}" + return extend_sign(val) elif self.symbols_by_addr.get(val) == "rib_nil": - return "nil" + return [] elif self.symbols_by_addr.get(val) == "rib_true": - return "#t" + return True elif self.symbols_by_addr.get(val) == "rib_false": - return "#f" + return False else: x, y, z = self.peek(val), self.peek(val+1), self.peek(val+2) if z == 0: # pair - return f"({self.show_obj(x)} : {self.show_obj(y)}" + return [self._obj(x)] + self._obj(y) elif z == 1: # proc MAX_PRIMITIVE = 31 if 0 < x < MAX_PRIMITIVE: @@ -87,9 +87,15 @@ def show_obj(self, val): else: return f"TODO: ({x}, {y}, {z})" - def show_stack(self): - """Show the contents of the stack, which is a list composed of ordinary pairs and continuation - ribs.""" + + def show_obj(self, val): + """Show an object, which may be an integer, special value, or rib.""" + + return str(self._obj(val)) + + + def stack(self): + """Contents of the stack, bottom to top.""" def go(addr): if self.symbols_by_addr.get(addr) == "rib_nil": @@ -102,9 +108,17 @@ def go(addr): x, y, z = self.peek(addr), self.peek(addr+1), self.peek(addr+2) if z == 0: # pair - return go(y) + [self.show_obj(x)] + return go(y) + [self._obj(x)] else: # A continuation: (stack, closure, next instr) return go(x) + [f"cont({self.show_addr(z)})"] SP = 0 - return ", ".join(go(self.peek(SP))) + return go(self.peek(SP)) + + + def show_stack(self): + """Show the contents of the stack, which is a list composed of ordinary pairs and continuation + ribs.""" + + return ", ".join(str(o) for o in self.stack()) + diff --git a/alt/scheme/test_rvm.py b/alt/scheme/test_rvm.py index 4118e20..43a7ce8 100755 --- a/alt/scheme/test_rvm.py +++ b/alt/scheme/test_rvm.py @@ -4,6 +4,7 @@ from alt import big from alt.scheme import rvm +from alt.scheme.inspector import Inspector import nand from nand.translate import AssemblySource from nand.vector import unsigned @@ -11,46 +12,37 @@ def test_trivial(): program = "42" - peek, output = run_to_halt(program, max_cycles=20000) + inspect, output = run_to_halt(program, max_cycles=20000) - assert get_stack(peek) == [42] + assert inspect.stack() == [42] assert output == [] def test_add(): program = "(+ 1 2)" - peek, output = run_to_halt(program, max_cycles=20000) + inspect, output = run_to_halt(program, max_cycles=20000) - assert get_stack(peek) == [3] + assert inspect.stack() == [3] assert output == [] def test_sub(): program = "(- 123 234)" - peek, output = run_to_halt(program, max_cycles=20000) + inspect, output = run_to_halt(program, max_cycles=20000) - assert get_stack(peek) == [-111] + assert inspect.stack() == [-111] assert output == [] +def test_quote(): + program = "'()" -def get_stack(peek): - def loop(addr): - car = peek(addr) - cdr = peek(addr+1) - type_ = peek(addr+2) - if type_ == 5: - # It's a "special", so assume it's the nil value - return [] - elif type_ == 0: - return loop(cdr) + [car] - else: - # must be a continuation rib: - return loop(car) + [f"cont@{addr}"] + inspect, output = run_to_halt(program, max_cycles=20000) - return loop(peek(0)) + assert inspect.stack() == [[]] + assert output == [] def run_to_halt(program, max_cycles=5000): @@ -104,11 +96,6 @@ def show_addr(addr): if computer.fetch: print(f"pc: {show_addr(computer.pc)}; PC: {show_addr(computer.peek(1))}") - def peek(addr): - """Read from RAM or ROM.""" - if big.ROM_BASE <= addr < big.HEAP_BASE: - return computer._rom.storage[addr] - else: - return computer.peek(addr) + inspect = Inspector(computer, symbols) - return (peek, output) + return (inspect, output) diff --git a/nand/vector.py b/nand/vector.py index 9816f63..e67c227 100644 --- a/nand/vector.py +++ b/nand/vector.py @@ -694,6 +694,14 @@ def poke_screen(self, address, value): self._screen.storage[address] = value + def peek_rom(self, address): + """Read a single word from the Computer's ROM.""" + + if self._rom is None: + raise MissingComponent("No ROM present") + + return self._rom.storage[address] + def set_keydown(self, keycode): """Provide the code which identifies a single key which is currently pressed.""" From 4a1300c3a8290d1cfe50102794b31503d9f90a54 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Wed, 7 Feb 2024 08:54:58 -0500 Subject: [PATCH 065/221] Useful tracing in tests --- alt/scheme/inspector.py | 60 ++++++++++++++++++++++++++++++++++++----- alt/scheme/test_rvm.py | 58 +++++++++++++++++++++++++++------------ 2 files changed, 94 insertions(+), 24 deletions(-) diff --git a/alt/scheme/inspector.py b/alt/scheme/inspector.py index 2492543..d372b4b 100644 --- a/alt/scheme/inspector.py +++ b/alt/scheme/inspector.py @@ -23,6 +23,8 @@ def show_addr(self, addr): else: return f"@{unsigned(addr)}" + def is_labeled(self, addr): + return addr in self.symbols_by_addr def show_instr(self, addr): x, y, z = self.peek(addr), self.peek(addr+1), self.peek(addr+2) @@ -35,7 +37,7 @@ def show_target(): if 0 <= y <= MAX_SLOT: return f"#{y}" else: - return self.show_addr(y) + return self.show_symbol(y) if x == 0 and z == 0: return f"jump {show_target()}" @@ -55,6 +57,13 @@ def show_target(): return f"not an instr: {(x, y, z)}" + def show_symbol(self, addr): + val, name, z = self.peek(addr), self.peek(addr+1), self.peek(addr+2) + assert z == 2 + rib_num = (addr - big.HEAP_BASE)//3 + return f'"{self._obj(name)}"{self.show_addr(addr)}(_{rib_num}) = {self._obj(val)}' + + def _obj(self, val): """Python representation of an object, which may be an integer, special value, or rib.""" @@ -72,15 +81,25 @@ def _obj(self, val): if z == 0: # pair return [self._obj(x)] + self._obj(y) elif z == 1: # proc - MAX_PRIMITIVE = 31 - if 0 < x < MAX_PRIMITIVE: - return f"primitive(#{x})" # TODO: lookup the primitive's name + if 0 <= x < len(PRIMITIVES): + return f"proc({PRIMITIVES[x]})" else: num_args, instr = self.peek(x), self.peek(x+2) return f"proc(args={num_args}, env={self.show_obj(y)}, instr={self.show_addr(instr)}){self.show_addr(val)}" - # elif z == 2: # symbol - # elif z == 3: # string - # elif z == 4: # vector + elif z == 2: # symbol + return f"symbol({self._obj(y)} = {self._obj(x)})" + elif z == 3: # string + chars = self._obj(x) + if len(chars) != y: + raise Exception("bad string") + else: + return "".join(chr(c) for c in chars) + elif z == 4: # vector + elems = self._obj(x) + if len(elems) != y: + raise Exception("bad vector") + else: + return elems elif z == 5: # Unexpected, but show the contents just in case return f"special({self.show_obj(x)}, {self.show_obj(y)}){self.show_addr(val)}" @@ -122,3 +141,30 @@ def show_stack(self): return ", ".join(str(o) for o in self.stack()) + +PRIMITIVES = { + 0: "rib", + 1: "id", + 2: "arg1", + 3: "arg2", + 4: "close", + 5: "rib?", + 6: "field0", + 7: "field1", + 8: "field2", + 9: "field0-set!", + 10: "field1-set!", + 11: "field2-set!", + 12: "eqv?", + 13: "<", + 14: "+", + 15: "-", + 16: "*", + 17: "quotient (unimp.)", + 18: "getchar (unimp.)", + 19: "putchar (unimp.)", + # Extra: + 20: "peek", + 21: "poke", + 22: "halt", +} \ No newline at end of file diff --git a/alt/scheme/test_rvm.py b/alt/scheme/test_rvm.py index 43a7ce8..304b5a4 100755 --- a/alt/scheme/test_rvm.py +++ b/alt/scheme/test_rvm.py @@ -18,6 +18,15 @@ def test_trivial(): assert output == [] +def test_string(): + program = '"abc"' + + inspect, output = run_to_halt(program, max_cycles=20000) + + assert inspect.stack() == ["abc"] + assert output == [] + + def test_add(): program = "(+ 1 2)" @@ -36,6 +45,7 @@ def test_sub(): assert output == [] + def test_quote(): program = "'()" @@ -45,6 +55,27 @@ def test_quote(): assert output == [] +def test_cons(): + program = "(cons 1 '(2))" + + inspect, output = run_to_halt(program, max_cycles=20000) + + assert inspect.stack() == [1, 2] + assert output == [] + + +def test_lambda(): + program = """ + ((lambda (x y) (+ x y)) + 14 28) + """ + + inspect, output = run_to_halt(program, max_cycles=20000) + + assert inspect.stack() == [42] + assert output == [] + + def run_to_halt(program, max_cycles=5000): """Compile and run a Scheme program, then return a function for inspecting the RAM, and a list of words that were written to the TTY port. @@ -61,19 +92,6 @@ def run_to_halt(program, max_cycles=5000): instrs, symbols, _ = big.assemble(asm.lines, min_static=None, builtins=rvm.BUILTINS) - symbols_by_addr = { addr: name for (name, addr) in symbols.items() } - - for addr, name in symbols_by_addr.items(): - print(f"{addr}: {name}") - - def show_addr(addr): - # addr = unsigned - if addr in symbols_by_addr: - return f"{symbols_by_addr[addr]} (@{addr})" - else: - return f"@{addr}" - - computer = nand.syntax.run(big.BigComputer, simulator="vector") computer.init_rom(instrs) @@ -85,6 +103,8 @@ def show_addr(addr): cycles = 0 output = [] + inspect = Inspector(computer, symbols) + while (not computer.fetch or computer.pc != symbols["halt_loop"]) and cycles <= max_cycles: computer.ticktock() cycles += 1 @@ -93,9 +113,13 @@ def show_addr(addr): if tty_char: output.append(tty_char) - if computer.fetch: - print(f"pc: {show_addr(computer.pc)}; PC: {show_addr(computer.peek(1))}") - - inspect = Inspector(computer, symbols) + if computer.fetch and inspect.is_labeled(computer.pc): + cpu_pc = inspect.show_addr(computer.pc) + print(f"{cpu_pc}") + if cpu_pc == "@exec_loop": + stack = ", ".join(str(x) for x in inspect.stack()) + print(f" stack: {stack}") + pc = inspect.peek(1) + print(f" {inspect.show_addr(pc):<10} {inspect.show_instr(pc)}") return (inspect, output) From 8ab5cae9a56c8fae9d24ad9ca0041c698559cd18 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Wed, 7 Feb 2024 09:18:22 -0500 Subject: [PATCH 066/221] Show the value of a symbol when it appears in jump/call --- alt/scheme/inspector.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/alt/scheme/inspector.py b/alt/scheme/inspector.py index d372b4b..a8823c9 100644 --- a/alt/scheme/inspector.py +++ b/alt/scheme/inspector.py @@ -29,7 +29,7 @@ def is_labeled(self, addr): def show_instr(self, addr): x, y, z = self.peek(addr), self.peek(addr+1), self.peek(addr+2) - def show_target(): + def show_target(with_value): """The target of a jump/call, get, or set: either a slot index or global.""" # FIXME: use the rvm's actual/current value @@ -37,16 +37,16 @@ def show_target(): if 0 <= y <= MAX_SLOT: return f"#{y}" else: - return self.show_symbol(y) + return self.show_symbol(y, with_value=with_value) if x == 0 and z == 0: - return f"jump {show_target()}" + return f"jump {show_target(True)}" elif x == 0: - return f"call {show_target()}" # -> {self.show_addr(z)}" + return f"call {show_target(True)}" # -> {self.show_addr(z)}" elif x == 1: - return f"set {show_target()}" # -> {self.show_addr(z)}" + return f"set {show_target(False)}" # -> {self.show_addr(z)}" elif x == 2: - return f"get {show_target()}" # -> {self.show_addr(z)}" + return f"get {show_target(False)}" # -> {self.show_addr(z)}" elif x == 3: return f"const {self.show_obj(y)}" # -> {self.show_addr(z)}" elif x == 4: @@ -57,11 +57,15 @@ def show_target(): return f"not an instr: {(x, y, z)}" - def show_symbol(self, addr): + def show_symbol(self, addr, with_value=False): val, name, z = self.peek(addr), self.peek(addr+1), self.peek(addr+2) assert z == 2 rib_num = (addr - big.HEAP_BASE)//3 - return f'"{self._obj(name)}"{self.show_addr(addr)}(_{rib_num}) = {self._obj(val)}' + name_and_addr = f'"{self._obj(name)}"{self.show_addr(addr)}(_{rib_num})' + if with_value: + return f"{name_and_addr} = {self._obj(val)}" + else: + return name_and_addr def _obj(self, val): From cc44a9360ce31d66485684f93f0d58474bff8df9 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Wed, 7 Feb 2024 18:42:44 -0500 Subject: [PATCH 067/221] Implement jump to closure --- alt/scheme/inspector.py | 2 +- alt/scheme/rvm.py | 160 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 153 insertions(+), 9 deletions(-) diff --git a/alt/scheme/inspector.py b/alt/scheme/inspector.py index a8823c9..6701187 100644 --- a/alt/scheme/inspector.py +++ b/alt/scheme/inspector.py @@ -134,7 +134,7 @@ def go(addr): return go(y) + [self._obj(x)] else: # A continuation: (stack, closure, next instr) - return go(x) + [f"cont({self.show_addr(z)})"] + return go(x) + [f"cont(saved={self._obj(x)}; {self._obj(y)}; {self.show_addr(z)}){self.show_addr(addr)}"] SP = 0 return go(self.peek(SP)) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index e855a0d..ffcffd6 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -264,6 +264,10 @@ def emit_vector(lbl, elems, count: int): emit_rib(lbl, elems, f"#{count}", "#4" asm.blank() + # Primordial continuation: + # x (stack) = [] + # y (proc) = 0 + # z (instr) = halt emit_rib("rib_outer_cont", "@rib_nil", "#0", "@instr_halt", "Bottom of stack: continuation to halt") asm.blank() @@ -401,7 +405,7 @@ def emit_instr(op, arg, next): # Low-memory "registers": "SP": 0, "PC": 1, - "NEXT_RIB": 2, + "NEXT_RIB": 2, # TODO: "LAST_RIB" is more often useful; you write some values, then you save the address somewhere "PRIMITIVE_CONT": 3, # where to go when primitive handler is done @@ -411,7 +415,7 @@ def emit_instr(op, arg, next): "TEMP_0": 5, "TEMP_1": 6, "TEMP_2": 7, - "TEMP_3": 7, + "TEMP_3": 8, # Useful values/addresses: "FIRST_RIB_MINUS_ONE": big.HEAP_BASE-1, @@ -644,8 +648,14 @@ def d_is_not_slot(): asm.instr("D=D&A") def find_cont(dest): + """Loop over the stack to find the first continuation (a non-pair.) + + No registers are affected except `dest`. + """ + cont_loop_test = asm.next_label("cont_loop_test") cont_loop_end = asm.next_label("cont_loop_end") + asm.comment("R5 = RAM[SP]") asm.instr("@SP") asm.instr("D=M") @@ -665,6 +675,50 @@ def find_cont(dest): asm.label(cont_loop_end) + def find_slot(dest): + """Loop over the stack to find the slot referred to by the current instruction, and placing + the location of the object in the supplied destination. + + Overwrites TEMP_0. + """ + assert dest != "TEMP_0" + + test_label = asm.next_label("slot_test") + end_label = asm.next_label("slot_end") + + asm.comment("R5 = idx") + y_to_d("PC") + asm.instr("@TEMP_0") + asm.instr("M=D") + + asm.comment(f"{dest} = SP") + asm.instr("@SP") + asm.instr("D=M") + asm.instr(f"@{dest}") + asm.instr("M=D") + + asm.label(test_label) + asm.instr("@TEMP_0") + asm.instr("D=M") + asm.instr(f"@{end_label}") + asm.instr("D;JLE") + + asm.comment(f"{dest} = cdr({dest})") + asm.instr(f"@{dest}") + asm.instr("A=M+1") + asm.instr("D=M") + asm.instr(f"@{dest}") + asm.instr("M=D") + # asm.instr("@KEYBOARD"); asm.instr("M=D") # DEBUG + + asm.comment("TEMP_0 -= 1") + asm.instr("@TEMP_0") + asm.instr("M=M-1") + asm.instr(f"@{test_label}") + asm.instr("0;JMP") + + asm.label(end_label) + asm.comment("First time: start with the 'next' of main (by falling through to continue_next)") @@ -733,16 +787,20 @@ def find_cont(dest): asm.label("proc_from_slot") asm.comment("TEMP_3 = proc rib from stack") - asm.comment("TODO") - asm.instr("@halt_loop") - asm.instr("0;JMP") + find_slot("TEMP_3") + asm.instr("@TEMP_3") # This has the address of the symbol + asm.instr("A=M") + asm.instr("D=M") + asm.instr("@TEMP_3") # Now update to the value (the proc) + asm.instr("M=D") asm.blank() asm.label("handle_proc_start") - asm.instr("D=D") # no-op to make the label tracable + asm.instr("D=D") # no-op to make the label traceable # TODO: if type_checking: asm.label("check_proc_rib") + # asm.instr("@TEMP_3"); asm.instr("D=M"); asm.instr("@KEYBOARD"); asm.instr("M=D") # DEBUG z_to_d("TEMP_3") asm.instr("D=D-1") asm.instr("@halt_loop") @@ -782,7 +840,6 @@ def find_cont(dest): asm.label("after_primitive_for_jump") asm.comment("find the continuation rib: first rib on stack with non-zero third field") - find_cont("TEMP_0") asm.comment("overwrite the top stack entry: SP.y = R5.x") @@ -799,8 +856,95 @@ def find_cont(dest): asm.instr("0;JMP") asm.blank() + asm.label("handle_jump_to_closure") - asm.instr("@halt_loop") + + asm.comment("find the continuation rib: first rib on stack with non-zero third field") + find_cont("TEMP_0") + + # New continuation rib. Can't update the existing continuation, in case it's the primordial + # one, which is stored in ROM. + # TODO: move it to RAM, so it can be updated in place? + asm.comment("TEMP_1 = new continuation = (old.x, proc, old.z)") + asm.instr("@NEXT_RIB") + asm.instr("D=M") + asm.instr("@TEMP_1") + asm.instr("M=D") + + asm.comment("New cont. saved stack = old.x") + asm.instr("@TEMP_0") + asm.instr("A=M") + asm.instr("D=M") + rib_append() + asm.comment("New cont. proc = TEMP_3") + asm.instr("@TEMP_3") + asm.instr("D=M") + rib_append() + asm.comment("New cont. next instr = old.z") + asm.instr("@TEMP_0") + asm.instr("A=M+1") + asm.instr("A=A+1") + asm.instr("D=M") + rib_append() + + + asm.comment("TEMP_2 = num_args (proc.x.x)") + asm.instr("@TEMP_3") + asm.instr("A=M") + asm.instr("A=M") + asm.instr("D=M") + asm.instr("@TEMP_2") + asm.instr("M=D") + # asm.instr("@KEYBOARD"); asm.instr("M=D") # DEBUG + + jump_params_test = asm.next_label("jump_params_test") + jump_params_end = asm.next_label("jump_params_end") + + asm.label(jump_params_test) + asm.instr("@TEMP_2") + asm.instr("D=M") + asm.instr(f"@{jump_params_end}") + asm.instr("D;JLE") + + asm.comment("pop one object and add it to the new stack") + pop("TEMP_0") + asm.instr("@TEMP_0") + asm.instr("D=M") + rib_append() + asm.instr("@TEMP_1") + asm.instr("D=M") + rib_append() + rib_append(0) + + asm.instr("@NEXT_RIB") + asm.instr("D=M") + asm.instr("@3") + asm.instr("D=D-A") + asm.instr("@TEMP_1") + asm.instr("M=D") + + asm.instr("@TEMP_2") + asm.instr("M=M-1") + asm.instr(f"@{jump_params_test}") + asm.instr("0;JMP") + asm.label(jump_params_end) + + asm.comment("Put new stack in place: SP = TEMP_1") + asm.instr("@TEMP_1") + asm.instr("D=M") + asm.instr("@SP") + asm.instr("M=D") + + asm.comment("PC = proc.x.z") + # TODO: PC = proc.x and goto continue_next? + asm.instr("@TEMP_3") + asm.instr("A=M") + asm.instr("A=M+1") + asm.instr("A=A+1") + asm.instr("D=M") + asm.instr("@PC") + asm.instr("M=D") + asm.instr("@exec_loop") asm.instr("0;JMP") asm.blank() From b87c4087505c98b13744e0508101e84de00b1761 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Wed, 7 Feb 2024 18:50:41 -0500 Subject: [PATCH 068/221] Implement get (slot) --- alt/scheme/rvm.py | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index ffcffd6..e816822 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -21,7 +21,7 @@ """Log every CPU instruction (in addition to COARSE and FINE logging.)""" -def run(program, print_asm=True, trace_level=TRACE_COARSE, verbose_tty=True): +def run(program, print_asm=True, trace_level=TRACE_FINE, verbose_tty=True): encoded = compile(program) # print(f"encoded program: {repr(encoded)}") @@ -1078,8 +1078,32 @@ def find_slot(dest): asm.blank() asm.label("handle_get_slot") - asm.comment("TODO") - asm.instr("@halt_loop") + find_slot("TEMP_3") + asm.instr("@TEMP_3") # This has the address of the stack entry + asm.instr("A=M") + asm.instr("D=M") + asm.instr("@TEMP_3") # Now update to the value + asm.instr("M=D") + asm.blank() + + # TODO: macro for push from TEMP + asm.comment("push TEMP3") + asm.instr("@TEMP_3") + asm.instr("D=M") + rib_append() + asm.instr("@SP") + asm.instr("D=M") + rib_append() + rib_append(0) + asm.comment("SP = NEXT_RIB-3 (the one we just initialized)") + asm.instr("@NEXT_RIB") + asm.instr("D=M") + asm.instr("@3") + asm.instr("D=D-A") + asm.instr("@SP") + asm.instr("M=D") + + asm.instr("@continue_next") asm.instr("0;JMP") asm.blank() From 3cd0f35cea26df06ce9bff878acffa2644720a2e Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Wed, 7 Feb 2024 18:55:57 -0500 Subject: [PATCH 069/221] =?UTF-8?q?Write=20a=20test=20for=20(define)=20tha?= =?UTF-8?q?t=E2=80=99s=20actually=20Scheme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- alt/scheme/test_rvm.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/alt/scheme/test_rvm.py b/alt/scheme/test_rvm.py index 304b5a4..38cba58 100755 --- a/alt/scheme/test_rvm.py +++ b/alt/scheme/test_rvm.py @@ -55,24 +55,28 @@ def test_quote(): assert output == [] -def test_cons(): - program = "(cons 1 '(2))" +def test_lambda(): + program = """ + ((lambda (x y) (+ x y)) + 14 28) + """ inspect, output = run_to_halt(program, max_cycles=20000) - assert inspect.stack() == [1, 2] + assert inspect.stack() == [42] assert output == [] -def test_lambda(): +def test_define(): program = """ - ((lambda (x y) (+ x y)) - 14 28) + (define (cons x y) (rib x y 0)) + + (cons 1 '(2)) """ inspect, output = run_to_halt(program, max_cycles=20000) - assert inspect.stack() == [42] + assert inspect.stack() == [[1, 2]] assert output == [] From 0f8f940c1b6105e7a2987ff660313454813f7682 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Thu, 8 Feb 2024 09:30:35 -0500 Subject: [PATCH 070/221] Implement poke --- alt/scheme/rvm.py | 41 ++++++++++++++++++++++++++++------------- alt/scheme/test_rvm.py | 26 ++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 13 deletions(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index e816822..670d3b3 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -479,6 +479,20 @@ def pop(dest): asm.instr("@SP") asm.instr("M=D") + def push(val="D"): + """""" + rib_append() + asm.instr("@SP") + asm.instr("D=M") + rib_append() + rib_append(0) # pair + asm.instr("@NEXT_RIB") + asm.instr("D=M") + asm.instr("@3") + asm.instr("D=D-A") + asm.instr("@SP") + asm.instr("M=D") + asm.label("start") asm.comment("NEXT_RIB = FIRST_RIB (HEAP_BASE)") @@ -1250,17 +1264,7 @@ def find_slot(dest): asm.instr("D=M") asm.instr("@3") asm.instr("D=D-A") - rib_append() - asm.instr("@SP") - asm.instr("D=M") - rib_append() - rib_append("0") # pair - asm.instr("@NEXT_RIB") - asm.instr("D=M") - asm.instr("@3") - asm.instr("D=D-A") - asm.instr("@SP") - asm.instr("M=D") + push("D") asm.instr("@PRIMITIVE_CONT") asm.instr("A=M") @@ -1407,8 +1411,19 @@ def find_slot(dest): asm.label("primitive_poke") asm.comment("primitive 20; poke :: x y -- y (and write the value y at RAM[x])") - asm.comment("TODO") - asm.instr("@halt_loop") + asm.comment("R5 = value") + pop("TEMP_0") + asm.comment("R6 = addr") + pop("TEMP_1") + asm.instr("@TEMP_0") + asm.instr("D=M") + asm.instr("@TEMP_1") + asm.instr("A=M") + asm.instr("M=D") + push("D") + + asm.instr("@PRIMITIVE_CONT") + asm.instr("A=M") asm.instr("0;JMP") asm.blank() diff --git a/alt/scheme/test_rvm.py b/alt/scheme/test_rvm.py index 38cba58..621b6f9 100755 --- a/alt/scheme/test_rvm.py +++ b/alt/scheme/test_rvm.py @@ -80,6 +80,32 @@ def test_define(): assert output == [] +def test_draw(): + program = """ + (define poke (rib 21 0 1)) + (define screen 1024) + (poke screen 65) + """ + + inspect, output = run_to_halt(program, max_cycles=20000) + + assert inspect.stack() == [65] + assert output == [] + + +def test_tty(): + program = """ + (define poke (rib 21 0 1)) + (define keyboard 2047) + (poke keyboard 48) + """ + + inspect, output = run_to_halt(program, max_cycles=20000) + + assert inspect.stack() == [48] + assert output == [ord('0')] + + def run_to_halt(program, max_cycles=5000): """Compile and run a Scheme program, then return a function for inspecting the RAM, and a list of words that were written to the TTY port. From 8e1c1898ad9f8899ccde19aa454a9fc4664edc32 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Fri, 9 Feb 2024 19:27:40 -0500 Subject: [PATCH 071/221] Summarize ROM space for the interpreter vs the program --- alt/scheme/rvm.py | 42 +++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 670d3b3..7934adf 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -20,15 +20,20 @@ TRACE_ALL = 3 """Log every CPU instruction (in addition to COARSE and FINE logging.)""" +DEFAULT_PRINT_ASM = False +DEFAULT_TRACE_LEVEL = TRACE_FINE -def run(program, print_asm=True, trace_level=TRACE_FINE, verbose_tty=True): + +def run(program, print_asm=DEFAULT_PRINT_ASM, trace_level=DEFAULT_TRACE_LEVEL, verbose_tty=True): encoded = compile(program) - # print(f"encoded program: {repr(encoded)}") + + if print_asm: + print(f"encoded program: {repr(encoded)}") run_compiled(encoded, print_asm, trace_level, verbose_tty) -def run_compiled(encoded, print_asm=True, trace_level=TRACE_COARSE, verbose_tty=True): +def run_compiled(encoded, print_asm=DEFAULT_PRINT_ASM, trace_level=DEFAULT_TRACE_LEVEL, verbose_tty=True): asm = AssemblySource() @@ -45,17 +50,6 @@ def run_compiled(encoded, print_asm=True, trace_level=TRACE_COARSE, verbose_tty= for l in asm.lines: print(l) print() - runtime_words = len(instrs) - big.ROM_BASE - total_rom = big.HEAP_BASE - big.ROM_BASE - # TODO: count ribs in the ROM separate from the fixed runtime - # program_words = len(encoded) - reserved_words = 8 + 22 # Interpreter/decoder state, and the primitive vectors - # available_ram = 0x4000 - reserved_words - print(f"ROM: {runtime_words:,d} ({100*runtime_words/total_rom:0.2f}%)") - # print(f"Encoded program: {program_words} ({100*program_words/available_ram:0.2f}%)") - print() - - def show_map(label, m): print("\n".join( [ f"{label}:" ] + @@ -70,6 +64,24 @@ def show_map(label, m): if statics != {}: show_map("Statics", statics) + print_summary = True + if print_summary: + interpreter_words = symbols["interpreter_end"] - big.ROM_BASE + program_words = len(instrs) - symbols["interpreter_end"] # Note: includes at least 5 pre-defined ribs + total_words = len(instrs) - big.ROM_BASE + rom_capacity = big.HEAP_BASE - big.ROM_BASE + # TODO: count ribs in the ROM separate from the fixed runtime + # program_words = len(encoded) + # reserved_words = 8 + 22 # Interpreter/decoder state, and the primitive vectors + # available_ram = 0x4000 - reserved_words + print(f"Interpreter: {interpreter_words:5,d} ({100*interpreter_words/rom_capacity:2.1f}%)") + print(f"Program: {program_words:5,d} ({100*program_words/rom_capacity:2.1f}%)") + print(f"Total ROM: {total_words:5,d} ({100*total_words/rom_capacity:2.1f}%)") + print() + + + + last_traced_exec = None symbols_by_addr = { addr: name for (name, addr) in symbols.items() } @@ -368,7 +380,7 @@ def emit_instr(op, arg, next): break else: params_lbl = asm.next_label("params") - print(f"{repr(n)}; {body}") + # print(f"{repr(n)}; {body}") emit_rib(params_lbl, f"#{n}", "#0", body) # FIXME: is this even close? proc_label = asm.next_label("proc") From be44e34d6a2761d44e35150a8ebfe03861bb4ac7 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Fri, 9 Feb 2024 19:27:53 -0500 Subject: [PATCH 072/221] Check for no trace fn --- alt/big.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/alt/big.py b/alt/big.py index 04e7315..0e0d7de 100755 --- a/alt/big.py +++ b/alt/big.py @@ -346,7 +346,8 @@ def run(program, chip=BigComputer, name="Flat!", font="monaco-9", halt_addr=None if not halted and computer.pc == halt_addr: halted = True print(f"\nHalted after {cycles} cycles\n") - trace(computer, cycles) + if trace is not None: + trace(computer, cycles) if not halted: From ef9a446ccef90b29cf138b2e0a53d53d4451e6fc Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Fri, 9 Feb 2024 19:30:35 -0500 Subject: [PATCH 073/221] Factor out common loop sequences --- alt/scheme/rvm.py | 117 +++++++++++++++------------------------------- 1 file changed, 38 insertions(+), 79 deletions(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 7934adf..e959920 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -746,6 +746,13 @@ def find_slot(dest): asm.label(end_label) + def unimp(): + asm.comment("TODO") + asm.instr("@halt_loop") + asm.instr("0;JMP") + asm.blank() + + asm.comment("First time: start with the 'next' of main (by falling through to continue_next)") asm.comment("Typical loop path: get the next instruction to interpret from the third field of the current instruction:") @@ -1069,10 +1076,7 @@ def find_slot(dest): asm.blank() asm.label("handle_set_slot") - asm.comment("TODO") - asm.instr("@halt_loop") - asm.instr("0;JMP") - asm.blank() + unimp() asm.label("opcode_2") asm.comment("type 2: get") @@ -1162,10 +1166,7 @@ def find_slot(dest): asm.label("opcode_4") asm.comment("type 4: if") - asm.comment("TODO") - asm.instr("@halt_loop") - asm.instr("0;JMP") - asm.blank() + unimp() # Note: a safety check would have the same result, but this makes it explicit in case we ever # make those checks optional. @@ -1255,6 +1256,12 @@ def find_slot(dest): asm.blank() + def return_from_primitive(): + asm.instr("@PRIMITIVE_CONT") + asm.instr("A=M") + asm.instr("0;JMP") + asm.blank() + asm.label("primitive_rib") asm.comment("primitive 0; rib :: x y z -- rib(x, y, z)") pop("TEMP_2") @@ -1277,103 +1284,70 @@ def find_slot(dest): asm.instr("@3") asm.instr("D=D-A") push("D") - - asm.instr("@PRIMITIVE_CONT") - asm.instr("A=M") - asm.instr("0;JMP") - asm.blank() + return_from_primitive() asm.label("primitive_id") asm.comment("primitive 1; id :: x -- x") asm.comment("... and, that's all folks") - asm.instr("@PRIMITIVE_CONT") - asm.instr("A=M") - asm.instr("0;JMP") - asm.blank() + return_from_primitive() asm.label("primitive_arg1") asm.comment("primitive 2; arg1 :: x y -- x") asm.comment("TODO") - asm.instr("@halt_loop") - asm.instr("0;JMP") - asm.blank() + return_from_primitive() asm.label("primitive_arg2") asm.comment("primitive 3; arg2 :: x y -- y") asm.comment("TODO") - asm.instr("@halt_loop") - asm.instr("0;JMP") - asm.blank() + unimp() asm.label("primitive_close") asm.comment("primitive 4; close :: x -- rib(x[0], stack, 1)") asm.comment("TODO") - asm.instr("@halt_loop") - asm.instr("0;JMP") - asm.blank() + unimp() asm.label("primitive_rib?") asm.comment("primitive 5; rib? :: x -- bool(x is a rib)") asm.comment("TODO") - asm.instr("@halt_loop") - asm.instr("0;JMP") - asm.blank() + unimp() asm.label("primitive_field0") asm.comment("primitive 6; field0 :: rib(x, _, _) -- x") asm.comment("TODO") - asm.instr("@halt_loop") - asm.instr("0;JMP") - asm.blank() + unimp() asm.label("primitive_field1") asm.comment("primitive 7; field1 :: rib(_, y, _) -- y") asm.comment("TODO") - asm.instr("@halt_loop") - asm.instr("0;JMP") - asm.blank() + unimp() asm.label("primitive_field2") asm.comment("primitive 8; field2 :: rib(_, _, z) -- z") asm.comment("TODO") - asm.instr("@halt_loop") - asm.instr("0;JMP") - asm.blank() + unimp() asm.label("primitive_field0-set!") asm.comment("primitive 9; field0-set! :: rib(_, y, z) x -- x (and update the rib in place: rib(x, y, z))") asm.comment("TODO") - asm.instr("@halt_loop") - asm.instr("0;JMP") - asm.blank() + unimp() asm.label("primitive_field1-set!") asm.comment("primitive 10; field1-set! :: rib(x, _, z) y -- y (and update the rib in place: rib(x, y, z))") asm.comment("TODO") - asm.instr("@halt_loop") - asm.instr("0;JMP") - asm.blank() + unimp() asm.label("primitive_field2-set!") asm.comment("primitive 11; field2-set! :: rib(x, y, _) z -- z (and update the rib in place: rib(x, y, z))") asm.comment("TODO") - asm.instr("@halt_loop") - asm.instr("0;JMP") - asm.blank() + unimp() asm.label("primitive_eqv?") asm.comment("primitive 12; eqv? :: x y -- bool(x is identical to y)") - asm.comment("TODO") - asm.instr("@halt_loop") - asm.instr("0;JMP") - asm.blank() + unimp() asm.label("primitive_<") asm.comment("primitive 13; < :: x y -- bool(x < y)") - asm.comment("TODO") - asm.instr("@halt_loop") - asm.instr("0;JMP") - asm.blank() + unimp() asm.label("primitive_+") asm.comment("primitive 14; + :: x y -- x + y") @@ -1386,10 +1360,7 @@ def find_slot(dest): asm.instr("@SP") asm.instr("A=M") asm.instr("M=M+D") - asm.instr("@PRIMITIVE_CONT") - asm.instr("A=M") - asm.instr("0;JMP") - asm.blank() + return_from_primitive() asm.label("primitive_-") asm.comment("primitive 15; - :: x y -- x - y") @@ -1402,24 +1373,15 @@ def find_slot(dest): asm.instr("@SP") asm.instr("A=M") asm.instr("M=M-D") - asm.instr("@PRIMITIVE_CONT") - asm.instr("A=M") - asm.instr("0;JMP") - asm.blank() + return_from_primitive() asm.label("primitive_*") asm.comment("primitive 16; - :: x y -- x * y") - asm.comment("TODO") - asm.instr("@halt_loop") - asm.instr("0;JMP") - asm.blank() + unimp() asm.label("primitive_peek") asm.comment("primitive 19; peek :: x -- RAM[x]") - asm.comment("TODO") - asm.instr("@halt_loop") - asm.instr("0;JMP") - asm.blank() + unimp() asm.label("primitive_poke") asm.comment("primitive 20; poke :: x y -- y (and write the value y at RAM[x])") @@ -1433,20 +1395,17 @@ def find_slot(dest): asm.instr("A=M") asm.instr("M=D") push("D") - - asm.instr("@PRIMITIVE_CONT") - asm.instr("A=M") - asm.instr("0;JMP") - asm.blank() + return_from_primitive() asm.label("primitive_halt") asm.comment("primitive 21; halt :: -- (no more instructions are executed)") - asm.instr("@halt_loop") - asm.instr("0;JMP") - asm.blank() + unimp() asm.label("primitive_unimp") asm.comment("Note: the current instr will be logged if tracing is enabled") asm.instr("@halt_loop") asm.instr("0;JMP") asm.blank() + + asm.label("interpreter_end") + asm.blank() From ac2b4d4e3ae76b71fcd7209ee0243f5dcfd8cc6f Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Fri, 9 Feb 2024 19:31:32 -0500 Subject: [PATCH 074/221] Populate dummy primitive vector table entries --- alt/scheme/rvm.py | 1 + 1 file changed, 1 insertion(+) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index e959920..a1b8364 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -1252,6 +1252,7 @@ def unimp(): asm.comment("dummy handlers for 23-31 to simplify range check above:") for op in range(23, 32): asm.comment(f"{op} (dummy)") + asm.instr("@primitive_unimp") asm.label("primitive_vectors_end") asm.blank() From 4657139e31cc6dd824014c6940e280372d23f174 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Fri, 9 Feb 2024 19:31:49 -0500 Subject: [PATCH 075/221] Implement arg1 --- alt/scheme/rvm.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index a1b8364..8e584f0 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -1293,8 +1293,12 @@ def return_from_primitive(): return_from_primitive() asm.label("primitive_arg1") - asm.comment("primitive 2; arg1 :: x y -- x") - asm.comment("TODO") + asm.comment("primitive 2; arg1 :: x y -- x") # i.e. "drop" + asm.instr("@SP") + asm.instr("A=M+1") # addr of top entry.y + asm.instr("D=M") # addr of next entry + asm.instr("@SP") + asm.instr("M=D") return_from_primitive() asm.label("primitive_arg2") From 56baac38618c4b27dfc975ababc0c2b234b4533e Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Fri, 9 Feb 2024 19:32:11 -0500 Subject: [PATCH 076/221] Implement call (closure) --- alt/scheme/rvm.py | 175 ++++++++++++++++++++++++++-------------------- 1 file changed, 101 insertions(+), 74 deletions(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 8e584f0..3c10a42 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -477,14 +477,18 @@ def rib_append(val="D"): asm.instr(f"M={val}") def pop(dest): - asm.comment("TODO: check SP[2] == 0") - asm.comment(f"{dest} = SP[0]") + """Remove the top entry from the stack, placing the value in `dest`. + + Updates only SP and `dest`. + """ + asm.comment("TODO: check SP.z == 0") + asm.comment(f"{dest} = SP.x") asm.instr("@SP") asm.instr("A=M") asm.instr("D=M") asm.instr(f"@{dest}") asm.instr("M=D") - asm.comment("SP = SP[1]") + asm.comment("SP = SP.y") asm.instr("@SP") asm.instr("A=M+1") asm.instr("D=M") @@ -492,8 +496,11 @@ def pop(dest): asm.instr("M=D") def push(val="D"): - """""" - rib_append() + """Add a new entry to the stack with the value from D (or 0 or 1, in case that's ever useful.) + + Updates SP and NEXT_RIB. + """ + rib_append(val) asm.instr("@SP") asm.instr("D=M") rib_append() @@ -920,47 +927,67 @@ def unimp(): asm.instr("D=M") rib_append() + def wrangle_closure_params(): + """Move num_args objects from the current stack to a new stack on top of a new continuation rib. - asm.comment("TEMP_2 = num_args (proc.x.x)") - asm.instr("@TEMP_3") - asm.instr("A=M") - asm.instr("A=M") - asm.instr("D=M") - asm.instr("@TEMP_2") - asm.instr("M=D") - # asm.instr("@KEYBOARD"); asm.instr("M=D") # DEBUG + The continuation rib is not modified (but the reference to it in TEMP_1 is overwritten.) - jump_params_test = asm.next_label("jump_params_test") - jump_params_end = asm.next_label("jump_params_end") + Before: + TEMP_3 = addr of proc rib + TEMP_1 = addr of new continuation (just allocated) - asm.label(jump_params_test) - asm.instr("@TEMP_2") - asm.instr("D=M") - asm.instr(f"@{jump_params_end}") - asm.instr("D;JLE") + During: + TEMP_2 = loop var: num args remaining + TEMP_1 = loop var: top of new stack + TEMP_0 = overwritten - asm.comment("pop one object and add it to the new stack") - pop("TEMP_0") - asm.instr("@TEMP_0") - asm.instr("D=M") - rib_append() - asm.instr("@TEMP_1") - asm.instr("D=M") - rib_append() - rib_append(0) + After: + TEMP_1 = new top of stack + """ - asm.instr("@NEXT_RIB") - asm.instr("D=M") - asm.instr("@3") - asm.instr("D=D-A") - asm.instr("@TEMP_1") - asm.instr("M=D") + asm.comment("TEMP_2 = num_args (proc.x.x)") + asm.instr("@TEMP_3") + asm.instr("A=M") + asm.instr("A=M") + asm.instr("D=M") + asm.instr("@TEMP_2") + asm.instr("M=D") + # asm.instr("@KEYBOARD"); asm.instr("M=D") # DEBUG - asm.instr("@TEMP_2") - asm.instr("M=M-1") - asm.instr(f"@{jump_params_test}") - asm.instr("0;JMP") - asm.label(jump_params_end) + params_test = asm.next_label("params_test") + params_end = asm.next_label("params_end") + + asm.label(params_test) + asm.instr("@TEMP_2") + asm.instr("D=M") + asm.instr(f"@{params_end}") + asm.instr("D;JLE") + + # TODO: modify the stack entry in place? And fix up SP, then + asm.comment("pop one object and add it to the new stack") + pop("TEMP_0") + asm.instr("@TEMP_0") + asm.instr("D=M") + rib_append() + asm.instr("@TEMP_1") + asm.instr("D=M") + rib_append() + rib_append(0) + + asm.instr("@NEXT_RIB") + asm.instr("D=M") + asm.instr("@3") + asm.instr("D=D-A") + asm.instr("@TEMP_1") + asm.instr("M=D") + + asm.instr("@TEMP_2") + asm.instr("M=M-1") + asm.instr(f"@{params_test}") + asm.instr("0;JMP") + asm.label(params_end) + + wrangle_closure_params() asm.comment("Put new stack in place: SP = TEMP_1") asm.instr("@TEMP_1") @@ -969,7 +996,6 @@ def unimp(): asm.instr("M=D") asm.comment("PC = proc.x.z") - # TODO: PC = proc.x and goto continue_next? asm.instr("@TEMP_3") asm.instr("A=M") asm.instr("A=M+1") @@ -977,6 +1003,7 @@ def unimp(): asm.instr("D=M") asm.instr("@PC") asm.instr("M=D") + asm.instr("@exec_loop") asm.instr("0;JMP") asm.blank() @@ -1004,54 +1031,54 @@ def unimp(): asm.instr("0;JMP") asm.label("handle_call_closure") - asm.comment("R5 = new rib for the continuation") + + asm.comment("R6 = new rib for the continuation") asm.instr("@NEXT_RIB") asm.instr("D=M") - asm.instr("@TEMP_0") - rib_append(0) - rib_append(0) - rib_append(0) - asm.blank() - - asm.comment("- pop num_params values from stack, forming list with cont as tail:") # re-use the ribs? - asm.comment("R6 = num_params") - y_to_d("PC") # target proc asm.instr("@TEMP_1") asm.instr("M=D") - asm.comment("R7 = acc") - asm.instr("@TEMP_0") + rib_append(0) # cont.x gets filled in later + asm.instr("@TEMP_3") + asm.instr("D=M") + rib_append() # cont.y = TEMP_3 (the proc rib) + z_to_d("PC") + rib_append() # cont.z = pc.z (next instr after the call) + asm.blank() + + wrangle_closure_params() + + asm.comment("TEMP_2 = SP (old stack to save)") + asm.instr("@SP") asm.instr("D=M") asm.instr("@TEMP_2") asm.instr("M=D") - - asm.label("pop_params_loop") + asm.comment("SP = TEMP_1 (top of new stack)") asm.instr("@TEMP_1") asm.instr("D=M") - asm.instr("@pop_params_end") - asm.instr("D;JLE") - - - + asm.instr("@SP") + asm.instr("M=D") + asm.comment("cont.x = TEMP_2 (saved stack)") + find_cont("TEMP_1") # Hmm. Searching here just because we ran out of TEMPs to hold onto it + asm.instr("@TEMP_2") + asm.instr("D=M") asm.instr("@TEMP_1") - asm.instr("M=M-1") - asm.instr("@pop_params_loop") - asm.instr("0;JMP") - asm.label("pop_params_end") - asm.blank() - - asm.comment("- cont.x = SP") - asm.comment("- cont.y = PC.y (proc)") - asm.comment("- cont.z = PC.z") - asm.comment("- SP = arg_list") - asm.comment("- PC = PC.y.z (proc entry point)") + asm.instr("A=M") + asm.instr("M=D") + asm.comment("PC = proc.x.z") + asm.instr("@TEMP_3") + asm.instr("A=M") + asm.instr("A=M+1") + asm.instr("A=A+1") + asm.instr("D=M") + asm.instr("@PC") + asm.instr("M=D") - # TODO - # asm.instr("@continue_next") - asm.instr("@halt_loop") + asm.instr("@exec_loop") asm.instr("0;JMP") asm.blank() + asm.label("opcode_1") asm.comment("type 1: set") y_to_d("PC") From aa46f879f3ac0577eda123ba37818d13d8ba6cb2 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sat, 10 Feb 2024 12:44:39 -0500 Subject: [PATCH 077/221] Comments; random cleanup --- alt/scheme/rvm.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 3c10a42..e603274 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -2,6 +2,15 @@ """Ribbit VM implementation. +The interpreter and primitives are all hand-rolled assembly. In hindsight, it would have been +quicker and easier to adapt the compiler in "register.py", with a minimal library, and implement the +interpreter in that. The size in ROM of the actual interpreter turns out not to be very critical +(~1K words), and we're not really going for maximum performance here. + +RSC's encoded instruction stream is decoded (in Python) into ribs directly in the ROM, which +is both relatively efficient in time and space, and more realistic than loading a complex program +from some kind of I/O. + See http://www.iro.umontreal.ca/~feeley/papers/YvonFeeleyVMIL21.pdf for the general picture. """ @@ -70,19 +79,12 @@ def show_map(label, m): program_words = len(instrs) - symbols["interpreter_end"] # Note: includes at least 5 pre-defined ribs total_words = len(instrs) - big.ROM_BASE rom_capacity = big.HEAP_BASE - big.ROM_BASE - # TODO: count ribs in the ROM separate from the fixed runtime - # program_words = len(encoded) - # reserved_words = 8 + 22 # Interpreter/decoder state, and the primitive vectors - # available_ram = 0x4000 - reserved_words print(f"Interpreter: {interpreter_words:5,d} ({100*interpreter_words/rom_capacity:2.1f}%)") print(f"Program: {program_words:5,d} ({100*program_words/rom_capacity:2.1f}%)") print(f"Total ROM: {total_words:5,d} ({100*total_words/rom_capacity:2.1f}%)") print() - - - last_traced_exec = None symbols_by_addr = { addr: name for (name, addr) in symbols.items() } @@ -306,7 +308,6 @@ def symbol_ref(idx): The table is written from the end, and each entry is made of of two ribs, the `symbol` and a `pair`. """ - # TODO: fix the inevitable off-by-one error(s) here asm.comment(f'symbol_ref({idx}); "{sym_names[idx][1]}"') return f"#{big.HEAP_BASE + 6*(len(sym_names) - idx - 1)}" From 31f423dd8dd09c40fc4db684356627f839b6ec6b Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sat, 10 Feb 2024 13:11:45 -0500 Subject: [PATCH 078/221] Use whole word per char on screen --- alt/big.py | 60 ++++++++++++++++++++++++++---------------------------- 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/alt/big.py b/alt/big.py index 0e0d7de..c4fc33d 100755 --- a/alt/big.py +++ b/alt/big.py @@ -13,37 +13,38 @@ Writes to ROM addresses are ignored. A small portion of the RAM is reserved for screen buffer and I/O: -- 1000 words to hold 80x25 8-bit characters -- Keyboard and TTY in the same 1024-word "page". -- 23 other words are available for future expansion. +- 2000 words to hold 80x25 16-bit characters +- Keyboard and TTY in the same 2048-word "page". +- 47 other words are unused. Layout considerations: - ROM is only large enough to fit a scheme/basic/forth interpreter (8K?) -- Screen buffer and I/O: 1K +- Screen buffer and I/O: 2K - RAM fills as much of the rest of 64K as possible -- The low-memory page is RAM, for convenient access +- The low-memory page is RAM, for convenient access. - The ROM lives in the 15-bit addressable range, so a code address can always be loaded in one cycle - "negative" addresses are a uniform block of RAM, useful for heap -| Address Range | Size (words) | Storage | Contents | -| 0x0000–0x03FF | 1K | RAM | Temporaries, "registers", etc. | -| 0x0400–0x07FF | 1K | RAM | Screen buffer and I/O | -| 0x0800–0x7FFF | 30K | ROM | Code: boot/runtime; data | -| 0x8000–0xFFFF | 32K | RAM | Heap or other large blocks | +| Address Range | Size (words) | Storage | Contents | +| 0x0000–0x07FF | 2K | RAM | Temporaries, "registers", stack, etc. | +| 0x0800–0x0FFF | 2K | RAM | Screen buffer and I/O | +| 0x1000–0x7FFF | 28K | ROM | Code: boot/runtime; data | +| 0x8000–0xFFFF | 32K | RAM | Heap or other large blocks | Note: it's also possible to treat all negative values as addresses in a continous block of RAM starting at 0x8000 = -32768. In fact, this range extends all the way to the bottom of the ROM -at 0x0800 = 2048. +at 0x1000 = 4096. TODO: make the size of the ROM configurable, so you can trade off heap space vs runtime size. That means synthesizing the logic to overlay the right address range, so maybe you just select -one of a few available sizes, e.g. 2K, 6K, 14K, 30K? +one of a few available sizes, e.g. 4K, 12K, 28K? -"Character-mode" graphics make more efficient use of memory, with only 1k allocated to the screen -as opposed to 8K for a similar number of pixels (assuming 9-point font.) It also means at least -8x faster updates, +"Character-mode" graphics make more efficient use of memory, with only 2k allocated to the screen +as opposed to 8K for a similar number of pixels (assuming 9-point font.) To keep things simple and +quick, each 7-bit character is stored in a whole 16-bit word. No shifting or masking is necessary +to draw a single character on the screen. -For authentic Macintosh fonts, see https://archive.org/details/AppleMacintoshSystem753 +For authentic Macintosh fonts, see https://archive.org/details/AppleMacintoshSystem753. """ from nand import chip, lazy, RAM, ROM, Input, Output, DFF @@ -54,9 +55,9 @@ from nand.solutions import solved_06 from alt.threaded import Eq16 -SCREEN_BASE = 0x0400 -KEYBOARD_ADDR = 0x07FF -ROM_BASE = 0x0800 +SCREEN_BASE = 0x0800 +KEYBOARD_ADDR = 0x0FFF +ROM_BASE = 0x1000 HEAP_BASE = 0x8000 HEAP_TOP = 0xFFFF @@ -78,15 +79,14 @@ def FlatMemory(inputs, outputs): load = inputs.load address = inputs.address - # addresses 0x08-- through 0x7F-- are in the ROM: - # - high bits 00001–01111 (0000 1000 0000 0000 to 0111 1111 1111 1111) - # - or, 0... 1... .... .... - # - or, 1 <= (address >> 11) < 16 + # addresses 0x10-- through 0x7F-- are in the ROM: + # - high bits 0001–0111 (0001 0000 0000 0000 to 0111 1111 1111 1111) + # - or, 0..1 .... .... .... + # - or, 1 <= (address >> 12) < 8 is_rom = And(a=Not(in_=address[15]).out, b=Or(a=Or(a=address[14], b=address[13]).out, - b=Or(a=address[12], - b=address[11]).out).out).out + b=address[12]).out).out is_io = Eq16(a=address, b=KEYBOARD_ADDR).out @@ -396,7 +396,7 @@ def run(program, chip=BigComputer, name="Flat!", font="monaco-9", halt_addr=None class TextKVM(computer.KVM): """Keyboard and display, displaying characters using a set of baked-in glyphs. - Each word of the screen buffer stores a pair of 8-bit characters. + Each word of the screen buffer stores a single 16-bit character. """ def __init__(self, title, char_width, char_height, glyph_width, glyph_height, bitmap_path): @@ -413,12 +413,10 @@ def __init__(self, title, char_width, char_height, glyph_width, glyph_height, bi def update_display(self, get_chars): self.screen.fill(computer.COLORS[0]) - stride = self.char_width//2 for y in range(0, self.char_height): - for x in range(0, self.char_width, 2): - pair = get_chars(y*stride + x//2) - self.render( x, y, pair & 0xFF) - self.render(x+1, y, pair >> 8) + for x in range(0, self.char_width): + char = get_chars(y*self.char_width + x) + self.render(x, y, char) pygame.display.flip() From a47dbe2c2a97fdaf611054d7d44eb69ae6f93ab0 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sat, 10 Feb 2024 13:33:06 -0500 Subject: [PATCH 079/221] Update for simplified screen buffer --- alt/big/Output.asm | 584 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 473 insertions(+), 111 deletions(-) diff --git a/alt/big/Output.asm b/alt/big/Output.asm index 60f305d..b520633 100644 --- a/alt/big/Output.asm +++ b/alt/big/Output.asm @@ -21,23 +21,25 @@ D=A @SCREEN M=D -@16896 +@66 D=A -@1063 +@2127 M=D @67 D=A -@1984 +@3968 M=D -@17408 +@68 D=A -@2023 +@4047 M=D (copy_start) // DST = start of third row of screen buffer -@1104 +@SCREEN D=A +@240 +D=D+A @DST M=D // SRC = @table_start (start of the table in ROM) @@ -71,44 +73,84 @@ D;JLE @halt 0;JMP -// 40 words per line, for a header and then 8 lines for the lower half of the 8-bit table: +// 80 words per line, for a header and then 8 lines for the lower half of the 8-bit table: (table_start) // header #0 #0 #0 #0 -#0x3030 +#0x30 +#0x30 +#0 +#0x31 +#0x30 +#0 +#0x32 +#0x30 +#0 +#0x33 +#0x30 +#0 +#0x34 +#0x30 +#0 +#0x35 +#0x30 +#0 +#0x36 +#0x30 +#0 +#0x37 +#0x30 +#0 +#0x38 +#0x30 +#0 +#0x39 +#0x30 +#0 +#0x41 +#0x30 +#0 +#0x42 +#0x30 +#0 +#0x43 +#0x30 +#0 +#0x44 +#0x30 +#0 +#0x45 +#0x30 +#0 +#0x46 +#0x30 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 #0 -#0x3130 #0 -#0x3230 #0 -#0x3330 #0 -#0x3430 #0 -#0x3530 #0 -#0x3630 #0 -#0x3730 #0 -#0x3830 #0 -#0x3930 #0 -#0x4130 #0 -#0x4230 #0 -#0x4330 #0 -#0x4430 #0 -#0x4530 #0 -#0x4630 #0 #0 #0 @@ -117,8 +159,48 @@ D;JLE // 0x00–0F: #0 +#0x30 +#0x30 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 #0 -#0x3030 #0 #0 #0 @@ -159,8 +241,48 @@ D;JLE // 0x10–1F: #0 +#0x30 +#0x31 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 #0 -#0x3031 #0 #0 #0 @@ -201,40 +323,80 @@ D;JLE // 0x20–2F: #0 +#0x30 +#0x32 +#0 +#0 +#0x20 +#0 +#0 +#0x21 +#0 +#0 +#0x22 +#0 +#0 +#0x23 +#0 +#0 +#0x24 +#0 +#0 +#0x25 +#0 +#0 +#0x26 +#0 +#0 +#0x27 +#0 +#0 +#0x28 +#0 +#0 +#0x29 +#0 +#0 +#0x2A +#0 +#0 +#0x2B +#0 +#0 +#0x2C +#0 +#0 +#0x2D +#0 +#0 +#0x2E +#0 +#0 +#0x2F +#0 +#0 +#0 +#0 +#0 +#0 +#0 #0 -#0x3032 #0 -#0x0020 #0 -#0x8021 #0 -#0x0022 #0 -#0x0023 #0 -#0x0024 #0 -#0x0025 #0 -#0x0026 #0 -#0x0027 #0 -#0x0028 #0 -#0x0029 #0 -#0x002A #0 -#0x002B #0 -#0x002C #0 -#0x002D #0 -#0x002E #0 -#0x002F #0 #0 #0 @@ -243,40 +405,80 @@ D;JLE // 0x30–3F: #0 +#0x30 +#0x33 +#0 +#0 +#0x30 +#0 +#0 +#0x31 +#0 +#0 +#0x32 +#0 +#0 +#0x33 +#0 +#0 +#0x34 +#0 +#0 +#0x35 +#0 +#0 +#0x36 +#0 +#0 +#0x37 +#0 +#0 +#0x38 +#0 +#0 +#0x39 +#0 +#0 +#0x3A +#0 +#0 +#0x3B +#0 +#0 +#0x3C +#0 +#0 +#0x3D +#0 +#0 +#0x3E +#0 +#0 +#0x3F +#0 +#0 +#0 +#0 +#0 +#0 +#0 #0 -#0x3033 #0 -#0x0030 #0 -#0x0031 #0 -#0x0032 #0 -#0x0033 #0 -#0x0034 #0 -#0x0035 #0 -#0x0036 #0 -#0x0037 #0 -#0x0038 #0 -#0x0039 #0 -#0x003A #0 -#0x003B #0 -#0x003C #0 -#0x003D #0 -#0x003E #0 -#0x003F #0 #0 #0 @@ -285,40 +487,80 @@ D;JLE // 0x40–4F: #0 +#0x30 +#0x34 +#0 +#0 +#0x40 +#0 +#0 +#0x41 +#0 +#0 +#0x42 +#0 +#0 +#0x43 +#0 +#0 +#0x44 +#0 +#0 +#0x45 +#0 +#0 +#0x46 +#0 +#0 +#0x47 +#0 +#0 +#0x48 +#0 +#0 +#0x49 +#0 +#0 +#0x4A +#0 +#0 +#0x4B +#0 +#0 +#0x4C +#0 +#0 +#0x4D +#0 +#0 +#0x4E +#0 +#0 +#0x4F +#0 +#0 +#0 +#0 +#0 +#0 +#0 #0 -#0x3034 #0 -#0x0040 #0 -#0x0041 #0 -#0x0042 #0 -#0x0043 #0 -#0x0044 #0 -#0x0045 #0 -#0x0046 #0 -#0x0047 #0 -#0x0048 #0 -#0x0049 #0 -#0x004A #0 -#0x004B #0 -#0x004C #0 -#0x004D #0 -#0x004E #0 -#0x004F #0 #0 #0 @@ -327,82 +569,162 @@ D;JLE // 0x50–5F: #0 +#0x30 +#0x35 #0 -#0x3035 #0 #0x0050 #0 +#0 #0x0051 #0 +#0 #0x0052 #0 +#0 #0x0053 #0 +#0 #0x0054 #0 +#0 #0x0055 #0 +#0 #0x0056 #0 +#0 #0x0057 #0 +#0 #0x0058 #0 +#0 #0x0059 #0 +#0 #0x005A #0 +#0 #0x005B #0 +#0 #0x005C #0 +#0 #0x005D #0 +#0 #0x005E #0 +#0 #0x005F #0 #0 #0 #0 #0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 +#0 // 0x60–6F: #0 +#0x30 +#0x36 +#0 +#0 +#0x60 +#0 +#0 +#0x61 +#0 +#0 +#0x62 +#0 +#0 +#0x63 +#0 +#0 +#0x64 +#0 +#0 +#0x65 +#0 +#0 +#0x66 +#0 +#0 +#0x67 +#0 +#0 +#0x68 +#0 +#0 +#0x69 +#0 +#0 +#0x6A +#0 +#0 +#0x6B +#0 +#0 +#0x6C +#0 +#0 +#0x6D +#0 +#0 +#0x6E +#0 +#0 +#0x6F +#0 +#0 +#0 +#0 +#0 +#0 +#0 #0 -#0x3036 #0 -#0x0060 #0 -#0x0061 #0 -#0x0062 #0 -#0x0063 #0 -#0x0064 #0 -#0x0065 #0 -#0x0066 #0 -#0x0067 #0 -#0x0068 #0 -#0x0069 #0 -#0x006A #0 -#0x006B #0 -#0x006C #0 -#0x006D #0 -#0x006E #0 -#0x006F #0 #0 #0 @@ -411,40 +733,80 @@ D;JLE // 0x70–7F: #0 +#0x30 +#0x37 +#0 +#0 +#0x70 +#0 +#0 +#0x71 +#0 +#0 +#0x72 +#0 +#0 +#0x73 +#0 +#0 +#0x74 +#0 +#0 +#0x75 +#0 +#0 +#0x76 +#0 +#0 +#0x77 +#0 +#0 +#0x78 +#0 +#0 +#0x79 +#0 +#0 +#0x7A +#0 +#0 +#0x7B +#0 +#0 +#0x7C +#0 +#0 +#0x7D +#0 +#0 +#0x7E +#0 +#0 +#0x7F +#0 +#0 +#0 +#0 +#0 +#0 +#0 #0 -#0x3037 #0 -#0x0070 #0 -#0x0071 #0 -#0x0072 #0 -#0x0073 #0 -#0x0074 #0 -#0x0075 #0 -#0x0076 #0 -#0x0077 #0 -#0x0078 #0 -#0x0079 #0 -#0x007A #0 -#0x007B #0 -#0x007C #0 -#0x007D #0 -#0x007E #0 -#0x007F #0 #0 #0 From 4f677635c794bd64575d96a999507cc9ecfaf0c8 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 11 Feb 2024 12:13:50 -0500 Subject: [PATCH 080/221] Run a program from an .scm file --- alt/scheme/rvm.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index e603274..848a46d 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -1442,3 +1442,19 @@ def return_from_primitive(): asm.label("interpreter_end") asm.blank() + + +def main(): + import sys + + # TODO: command-line args, multiple source files, etc. + # --print, --trace + + with open(sys.argv[1]) as f: + program = "".join(f.readlines()) + + run(program) + + +if __name__ == "__main__": + main() From 177b51aed80d61f4fbf6b47c78723bcf5acd7b35 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 11 Feb 2024 12:58:05 -0500 Subject: [PATCH 081/221] Fix row/colum label digits --- alt/big/Output.asm | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/alt/big/Output.asm b/alt/big/Output.asm index b520633..1e66e26 100644 --- a/alt/big/Output.asm +++ b/alt/big/Output.asm @@ -83,50 +83,50 @@ D;JLE #0x30 #0x30 #0 -#0x31 #0x30 +#0x31 #0 -#0x32 #0x30 +#0x32 #0 -#0x33 #0x30 +#0x33 #0 -#0x34 #0x30 +#0x34 #0 -#0x35 #0x30 +#0x35 #0 -#0x36 #0x30 +#0x36 #0 -#0x37 #0x30 +#0x37 #0 -#0x38 #0x30 +#0x38 #0 -#0x39 #0x30 +#0x39 #0 -#0x41 #0x30 +#0x41 #0 -#0x42 #0x30 +#0x42 #0 -#0x43 #0x30 +#0x43 #0 -#0x44 #0x30 +#0x44 #0 -#0x45 #0x30 +#0x45 #0 -#0x46 #0x30 +#0x46 #0 #0 #0 @@ -241,8 +241,8 @@ D;JLE // 0x10–1F: #0 -#0x30 #0x31 +#0x30 #0 #0 #0 @@ -323,8 +323,8 @@ D;JLE // 0x20–2F: #0 -#0x30 #0x32 +#0x30 #0 #0 #0x20 @@ -405,8 +405,8 @@ D;JLE // 0x30–3F: #0 -#0x30 #0x33 +#0x30 #0 #0 #0x30 @@ -487,8 +487,8 @@ D;JLE // 0x40–4F: #0 -#0x30 #0x34 +#0x30 #0 #0 #0x40 @@ -569,8 +569,8 @@ D;JLE // 0x50–5F: #0 -#0x30 #0x35 +#0x30 #0 #0 #0x0050 @@ -651,8 +651,8 @@ D;JLE // 0x60–6F: #0 -#0x30 #0x36 +#0x30 #0 #0 #0x60 @@ -733,8 +733,8 @@ D;JLE // 0x70–7F: #0 -#0x30 #0x37 +#0x30 #0 #0 #0x70 From 92529b04bdddf540aee14ec64395b508ba2136cc Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 11 Feb 2024 13:28:02 -0500 Subject: [PATCH 082/221] Implement < --- alt/scheme/rvm.py | 27 ++++++++++++++++++++++++++- alt/scheme/test_rvm.py | 15 +++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 848a46d..625b5a1 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -1380,7 +1380,32 @@ def return_from_primitive(): asm.label("primitive_<") asm.comment("primitive 13; < :: x y -- bool(x < y)") - unimp() + # TODO: if type_checking: + asm.comment("TEMP_0 = pop() = y") + pop("TEMP_0") + asm.instr("@TEMP_0") + asm.instr("D=M") + asm.comment("TEMP_0 -= (SP.x = x)") + asm.instr("@SP") + asm.instr("A=M") # A = addr of SP.x + asm.instr("D=D-M") # D = y - x + # Note: range of ints is limited, so true overflow doesn't happen? + is_less_label = asm.next_label("is_less") + asm.instr(f"@{is_less_label}") + asm.instr("D;JGT") + asm.instr("@rib_false") + asm.instr("D=A") + asm.instr("@SP") + asm.instr("A=M") + asm.instr("M=D") + return_from_primitive() + asm.label(is_less_label) + asm.instr("@rib_true") + asm.instr("D=A") + asm.instr("@SP") + asm.instr("A=M") + asm.instr("M=D") + return_from_primitive() asm.label("primitive_+") asm.comment("primitive 14; + :: x y -- x + y") diff --git a/alt/scheme/test_rvm.py b/alt/scheme/test_rvm.py index 621b6f9..14bd047 100755 --- a/alt/scheme/test_rvm.py +++ b/alt/scheme/test_rvm.py @@ -27,6 +27,21 @@ def test_string(): assert output == [] +def test_lt(): + program = """ + (define (cons x y) (rib x y 0)) + (cons (< 1 2) + (cons (< 1 1) + (cons (< 2 1) + '()))) + """ + + inspect, output = run_to_halt(program, max_cycles=20000) + + assert inspect.stack() == [[True, False, False]] + assert output == [] + + def test_add(): program = "(+ 1 2)" From 35542eed1b9bb08853c99ee1df20b8d7cf72686d Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 11 Feb 2024 13:28:51 -0500 Subject: [PATCH 083/221] Tolerate unexpected pairs (as in continuations) --- alt/scheme/inspector.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/alt/scheme/inspector.py b/alt/scheme/inspector.py index 6701187..69311e0 100644 --- a/alt/scheme/inspector.py +++ b/alt/scheme/inspector.py @@ -83,7 +83,12 @@ def _obj(self, val): else: x, y, z = self.peek(val), self.peek(val+1), self.peek(val+2) if z == 0: # pair - return [self._obj(x)] + self._obj(y) + car = self._obj(x) + cdr = self._obj(y) + if isinstance(cdr, list): + return [car] + cdr + else: + return (car, cdr) elif z == 1: # proc if 0 <= x < len(PRIMITIVES): return f"proc({PRIMITIVES[x]})" From 5be1a389c6e1f866a39018216dc8ec517b822f9f Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 11 Feb 2024 13:29:09 -0500 Subject: [PATCH 084/221] Clean up redundant TODOs --- alt/scheme/rvm.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 625b5a1..a92b655 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -1331,47 +1331,38 @@ def return_from_primitive(): asm.label("primitive_arg2") asm.comment("primitive 3; arg2 :: x y -- y") - asm.comment("TODO") unimp() asm.label("primitive_close") asm.comment("primitive 4; close :: x -- rib(x[0], stack, 1)") - asm.comment("TODO") unimp() asm.label("primitive_rib?") asm.comment("primitive 5; rib? :: x -- bool(x is a rib)") - asm.comment("TODO") unimp() asm.label("primitive_field0") asm.comment("primitive 6; field0 :: rib(x, _, _) -- x") - asm.comment("TODO") unimp() asm.label("primitive_field1") asm.comment("primitive 7; field1 :: rib(_, y, _) -- y") - asm.comment("TODO") unimp() asm.label("primitive_field2") asm.comment("primitive 8; field2 :: rib(_, _, z) -- z") - asm.comment("TODO") unimp() asm.label("primitive_field0-set!") asm.comment("primitive 9; field0-set! :: rib(_, y, z) x -- x (and update the rib in place: rib(x, y, z))") - asm.comment("TODO") unimp() asm.label("primitive_field1-set!") asm.comment("primitive 10; field1-set! :: rib(x, _, z) y -- y (and update the rib in place: rib(x, y, z))") - asm.comment("TODO") unimp() asm.label("primitive_field2-set!") asm.comment("primitive 11; field2-set! :: rib(x, y, _) z -- z (and update the rib in place: rib(x, y, z))") - asm.comment("TODO") unimp() asm.label("primitive_eqv?") From ae3f6cdf02e1d080be619887b27f78536fecc075 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 11 Feb 2024 13:29:34 -0500 Subject: [PATCH 085/221] =?UTF-8?q?Default=20to=20=E2=80=94print=20and=20?= =?UTF-8?q?=E2=80=94trace?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- alt/scheme/rvm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index a92b655..10ed224 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -1469,7 +1469,7 @@ def main(): with open(sys.argv[1]) as f: program = "".join(f.readlines()) - run(program) + run(program, print_asm=True, trace_level=TRACE_COARSE) if __name__ == "__main__": From d0967ae2c99ccab28201f7ed86c9ef29f19d7420 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 11 Feb 2024 13:30:03 -0500 Subject: [PATCH 086/221] Update tests for simplified screen buffer --- alt/test_big.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/alt/test_big.py b/alt/test_big.py index 53d01d5..d0e4636 100755 --- a/alt/test_big.py +++ b/alt/test_big.py @@ -117,12 +117,13 @@ def test_rom(): mem = nand.run(FlatMemory, simulator="vector") # Writes to any ROM-mapped address are ignored: - for addr in (ROM_BASE, ROM_BASE + 1234, ROM_BASE + 29000, HEAP_BASE - 1): + for addr in (ROM_BASE, ROM_BASE + 1234, ROM_BASE + 27*1024, HEAP_BASE - 1): mem.address = addr mem.in_ = -1 mem.load = 1 mem.ticktock() + print(addr, mem.out) assert mem.out == 0 @@ -147,7 +148,7 @@ def test_gates_mem(): This would have been extra chips on the board, not extra gates in the CPU, presumably. """ - assert gate_count(FlatMemory)['nands'] == 222 + assert gate_count(FlatMemory)['nands'] == 219 import project_05 assert gate_count(project_05.MemorySystem)['nands'] == 163 @@ -674,7 +675,7 @@ def test_gates_computer(): """Overall extra chip size.""" # Note: factoring out instruction decoding seems to have added 2 gates - assert gate_count(BigComputer)['nands'] == 1450 + assert gate_count(BigComputer)['nands'] == 1447 import project_05 assert gate_count(project_05.Computer)['nands'] == 1262 From 5e2cdaa34e4cd801bc676bb2cc1ca13356802a21 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 11 Feb 2024 13:44:15 -0500 Subject: [PATCH 087/221] Test three trivial cases for < --- alt/scheme/test_rvm.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/alt/scheme/test_rvm.py b/alt/scheme/test_rvm.py index 14bd047..9246da7 100755 --- a/alt/scheme/test_rvm.py +++ b/alt/scheme/test_rvm.py @@ -28,13 +28,7 @@ def test_string(): def test_lt(): - program = """ - (define (cons x y) (rib x y 0)) - (cons (< 1 2) - (cons (< 1 1) - (cons (< 2 1) - '()))) - """ + program = several("(< 1 2)", "(< 1 1)", "(< 2 1)") inspect, output = run_to_halt(program, max_cycles=20000) @@ -121,6 +115,18 @@ def test_tty(): assert output == [ord('0')] + +def several(*exprs): + """Make a program that evaluates several expressions and constructs a list with each result.""" + def go(xs): + # print(xs) + if xs == []: + return " '()" + else: + return f"(cons {xs[0]}\n{go(xs[1:])})" + return "(define (cons x y) (rib x y 0))\n" + go(list(exprs)) + + def run_to_halt(program, max_cycles=5000): """Compile and run a Scheme program, then return a function for inspecting the RAM, and a list of words that were written to the TTY port. From f329e02f93a264ebe28ac01f1c83d717a075fa65 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 11 Feb 2024 13:44:36 -0500 Subject: [PATCH 088/221] Update TTY test for new address --- alt/scheme/test_rvm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alt/scheme/test_rvm.py b/alt/scheme/test_rvm.py index 9246da7..60fbc29 100755 --- a/alt/scheme/test_rvm.py +++ b/alt/scheme/test_rvm.py @@ -105,7 +105,7 @@ def test_draw(): def test_tty(): program = """ (define poke (rib 21 0 1)) - (define keyboard 2047) + (define keyboard 4095) (poke keyboard 48) """ From 798f3f124560f25c23a49c8bee9ee13566c3a544 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 11 Feb 2024 13:52:57 -0500 Subject: [PATCH 089/221] Start porting the character table example to Scheme --- alt/scheme/example/output.scm | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 alt/scheme/example/output.scm diff --git a/alt/scheme/example/output.scm b/alt/scheme/example/output.scm new file mode 100644 index 0000000..728b862 --- /dev/null +++ b/alt/scheme/example/output.scm @@ -0,0 +1,27 @@ +(define poke (rib 21 0 1)) + +;; With a '*2' primitive, this wouldn't be such a terrible idea... +;; 16*5x = 16*(4*x + x) = 2*(2*(2*(2*(2*(2*x)) + x))) +(define (times2 x) (+ x x)) +(define (times80 x) (times2 (times2 (times2 (times2 + (+ (times2 (times2 x)) + x)))))) + +(define screen 2048) +(define (drawchar x y c) (poke (+ screen (+ x (times80 y))) c)) + +(drawchar 0 0 65) +(drawchar 79 0 66) +(drawchar 0 24 67) +(drawchar 79 24 68) + +;; About 40k cycles and 2% of the heap to get this far + + + +;; TODO: character map table: +;; 00 01 02 ... +;; ... +;; 30 0 1 2 ... +;; 40 @ A B ... +;; ... \ No newline at end of file From 809b83a97a1b5cb0280c00411c4310329ecef3d9 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Tue, 13 Feb 2024 09:20:48 -0500 Subject: [PATCH 090/221] =?UTF-8?q?Implement=20=E2=80=9Cclose=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- alt/scheme/rvm.py | 23 ++++++++++++++++++++++- alt/scheme/test_rvm.py | 12 ++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 10ed224..0912ced 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -1335,7 +1335,28 @@ def return_from_primitive(): asm.label("primitive_close") asm.comment("primitive 4; close :: x -- rib(x[0], stack, 1)") - unimp() + asm.comment("TEMP_0 = new closure/proc rib") + asm.instr("@NEXT_RIB") + asm.instr("D=M") + asm.instr("@TEMP_0") + asm.instr("M=D") + asm.comment("TEMP_0.x = TOS.x") + x_to_d("SP") + asm.instr("A=D") + asm.instr("D=M") + rib_append() + asm.comment("TEMP_0.y = SP.y") + y_to_d("SP") + rib_append() + asm.comment("TEMP_0.z = 1 (proc type)") + rib_append("1") + asm.comment("Modify top stack entry in place") + asm.instr("@TEMP_0") + asm.instr("D=M") + asm.instr("@SP") + asm.instr("A=M") + asm.instr("M=D") + return_from_primitive() asm.label("primitive_rib?") asm.comment("primitive 5; rib? :: x -- bool(x is a rib)") diff --git a/alt/scheme/test_rvm.py b/alt/scheme/test_rvm.py index 60fbc29..c50323d 100755 --- a/alt/scheme/test_rvm.py +++ b/alt/scheme/test_rvm.py @@ -89,6 +89,18 @@ def test_define(): assert output == [] +def test_capture(): + program = """ + (define (add x) (lambda (y) (+ x y))) + ((add 1) 2) + """ + + inspect, output = run_to_halt(program, max_cycles=20000) + + assert inspect.stack() == [3] + assert output == [] + + def test_draw(): program = """ (define poke (rib 21 0 1)) From 56226c2edd68e883699f8d0b95fe9f4575fa3e61 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Tue, 13 Feb 2024 09:35:35 -0500 Subject: [PATCH 091/221] =?UTF-8?q?Fix=20decoding:=20don=E2=80=99t=20drop?= =?UTF-8?q?=20=E2=80=9Cif=E2=80=9D=20on=20the=20floor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- alt/scheme/rvm.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 0912ced..f4f17aa 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -399,8 +399,8 @@ def emit_instr(op, arg, next): if isinstance(n, int): n = f"#{n}" - instr_lbl = emit_instr(op-1, n, pop()) - push(f"@{instr_lbl}") + instr_lbl = emit_instr(op-1, n, pop()) + push(f"@{instr_lbl}") # This will be the body of the outer proc, so just "jump" straight to it: start_instr = n From 12f87451620992b374dd05cbf4f61871f16e211d Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Tue, 13 Feb 2024 09:42:50 -0500 Subject: [PATCH 092/221] =?UTF-8?q?Implement=20=E2=80=9Cif=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- alt/scheme/rvm.py | 16 +++++++++++++++- alt/scheme/test_rvm.py | 8 ++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index f4f17aa..86e8095 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -1194,7 +1194,21 @@ def wrangle_closure_params(): asm.label("opcode_4") asm.comment("type 4: if") - unimp() + pop("TEMP_0") + asm.comment("TOS is #f; no branch") + asm.instr("@TEMP_0") + asm.instr("D=M") + asm.instr("@rib_false") + asm.instr("D=D-A") + asm.instr("@continue_next") + asm.instr("D;JEQ") + asm.comment("PC = PC.y") + y_to_d("PC") + asm.instr("@PC") + asm.instr("M=D") + asm.instr("@exec_loop") + asm.instr("0;JMP") + asm.blank() # Note: a safety check would have the same result, but this makes it explicit in case we ever # make those checks optional. diff --git a/alt/scheme/test_rvm.py b/alt/scheme/test_rvm.py index c50323d..e0c0d00 100755 --- a/alt/scheme/test_rvm.py +++ b/alt/scheme/test_rvm.py @@ -54,6 +54,14 @@ def test_sub(): assert output == [] +def test_if(): + program = "(if #t 42 '())" + + inspect, output = run_to_halt(program, max_cycles=20000) + + assert inspect.stack() == [42] + assert output == [] + def test_quote(): program = "'()" From 65888dba83a679683da2c16cda38dcdcc8ae6345 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Tue, 13 Feb 2024 10:54:30 -0500 Subject: [PATCH 093/221] =?UTF-8?q?Implement=20=E2=80=9C*=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- alt/scheme/rvm.py | 68 +++++++++++++++++++++++++++++++++++++++++- alt/scheme/test_rvm.py | 18 +++++++++++ 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 86e8095..392d83e 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -1459,9 +1459,75 @@ def return_from_primitive(): asm.instr("M=M-D") return_from_primitive() + asm.label("primitive_*") asm.comment("primitive 16; - :: x y -- x * y") - unimp() + asm.comment("TEMP_0 = pop() = y") + pop("TEMP_0") + asm.comment("TEMP_1 = SP.x = x") + x_to_d("SP") + asm.instr("@TEMP_1") + asm.instr("M=D") + + asm.comment("TEMP_2 = bit mask = 1") + asm.instr("@TEMP_2") + asm.instr("M=1") + + asm.comment("TEMP_3 = acc = 0") + asm.instr("@TEMP_3") + asm.instr("M=0") + + mul_test_label = asm.next_label("mul_test") + mul_end_label = asm.next_label("mul_end") + mul_next_label = asm.next_label("mul_next") + + asm.label(mul_test_label) + asm.comment("if y == 0, exit") + asm.instr("@TEMP_0") + asm.instr("D=M") + asm.instr(f"@{mul_end_label}") + asm.instr("D;JEQ") + + asm.comment("if y & mask != 0, acc += (shifted) x") + asm.instr("@TEMP_0") + asm.instr("D=M") + asm.instr("@TEMP_2") + asm.instr("D=D&M") + asm.instr(f"@{mul_next_label}") + asm.instr("D;JEQ") + + asm.instr("@TEMP_1") + asm.instr("D=M") + asm.instr("@TEMP_3") + asm.instr("M=D+M") + + asm.label(mul_next_label) + asm.comment("y &= ~mask") + asm.instr("@TEMP_2") + asm.instr("D=!M") + asm.instr("@TEMP_0") + asm.instr("M=M&D") + asm.comment("mask <<= 1") + asm.instr("@TEMP_2") + asm.instr("D=M") + asm.instr("M=M+D") + asm.comment("x <<= 1") + asm.instr("@TEMP_1") + asm.instr("D=M") + asm.instr("M=M+D") + + asm.instr(f"@{mul_test_label}") + asm.instr("0;JMP") + + asm.label(mul_end_label) + asm.comment("SP.x = acc") + asm.instr("@TEMP_3") + asm.instr("D=M") + asm.instr("@SP") + asm.instr("A=M") + asm.instr("M=D") + return_from_primitive() + asm.label("primitive_peek") asm.comment("primitive 19; peek :: x -- RAM[x]") diff --git a/alt/scheme/test_rvm.py b/alt/scheme/test_rvm.py index e0c0d00..f205bf7 100755 --- a/alt/scheme/test_rvm.py +++ b/alt/scheme/test_rvm.py @@ -54,6 +54,24 @@ def test_sub(): assert output == [] +def test_mul(): + program = "(* 6 7)" + + inspect, output = run_to_halt(program, max_cycles=20000) + + assert inspect.stack() == [42] + assert output == [] + + +def test_mul_mixed_signs(): + program = several("(* 6 -7)", "(* -14 -3)", "(* 2 -21)") + + inspect, output = run_to_halt(program, max_cycles=20000) + + assert inspect.stack() == [[-42, 42, -42]] + assert output == [] + + def test_if(): program = "(if #t 42 '())" From f22871cfaf04e54457f42774cbcb99383ebc9c9b Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Tue, 13 Feb 2024 10:55:14 -0500 Subject: [PATCH 094/221] Test a first non-trivial function: factorial --- alt/scheme/test_rvm.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/alt/scheme/test_rvm.py b/alt/scheme/test_rvm.py index f205bf7..d4545dc 100755 --- a/alt/scheme/test_rvm.py +++ b/alt/scheme/test_rvm.py @@ -115,6 +115,21 @@ def test_define(): assert output == [] +def test_fact(): + program = """ + (define (fact n) + (if (< n 2) 1 + (* n (fact (- n 1))))) + + (fact 5) + """ + + inspect, output = run_to_halt(program, max_cycles=20000) + + assert inspect.stack() == [120] + assert output == [] + + def test_capture(): program = """ (define (add x) (lambda (y) (+ x y))) From c4f6eeccd11cea9339e393b80e6b3e8b01a9ee49 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Tue, 13 Feb 2024 11:02:27 -0500 Subject: [PATCH 095/221] =?UTF-8?q?Don=E2=80=99t=20try=20to=20run=20pytest?= =?UTF-8?q?=20over=20Ribbit=E2=80=99s=20Python=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/pythonapp.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pythonapp.yml b/.github/workflows/pythonapp.yml index e8d5fa3..430a595 100644 --- a/.github/workflows/pythonapp.yml +++ b/.github/workflows/pythonapp.yml @@ -23,7 +23,7 @@ jobs: pip install -r requirements.txt - name: Test with pytest run: | - pytest -v --doctest-modules + pytest -v --doctest-modules --ignore alt/scheme/ribbit - name: Lint with flake8 run: | pip install flake8 From 601c7e9ce0b37c3851e8c82bad9c0bd2b184f50c Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Tue, 13 Feb 2024 11:16:18 -0500 Subject: [PATCH 096/221] Fixup for decoding constants over 3 --- alt/scheme/rvm.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 392d83e..f26b391 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -382,6 +382,10 @@ def emit_instr(op, arg, next): else: params_lbl = asm.next_label("params") # print(f"{repr(n)}; {body}") + # HACK: somehow values over 3 are already strings + if isinstance(n, str) and n.startswith("#"): + print(f"already hash-prefixed: {n}") + n = n[1:] emit_rib(params_lbl, f"#{n}", "#0", body) # FIXME: is this even close? proc_label = asm.next_label("proc") From e014695520920a7b62271ff19393646c5f9af6a4 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Tue, 13 Feb 2024 11:16:40 -0500 Subject: [PATCH 097/221] Print the interpreter assembly before trying to parse it --- alt/scheme/rvm.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index f26b391..ae17401 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -50,15 +50,15 @@ def run_compiled(encoded, print_asm=DEFAULT_PRINT_ASM, trace_level=DEFAULT_TRACE decode(encoded, asm) + if print_asm: + for l in asm.lines: print(l) + print() + instrs, symbols, statics = big.assemble(asm.lines, min_static=None, builtins=BUILTINS) assert symbols["start"] == big.ROM_BASE - if print_asm: - for l in asm.lines: print(l) - print() - def show_map(label, m): print("\n".join( [ f"{label}:" ] + From a3386ab9cd8357095ef10c7dc1f8cfc0e42973ef Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Tue, 13 Feb 2024 11:16:47 -0500 Subject: [PATCH 098/221] Random cleanup --- alt/scheme/rvm.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index ae17401..0f01b95 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -393,8 +393,6 @@ def emit_instr(op, arg, next): asm.instr(f"@{params_lbl}") asm.instr("@rib_nil") asm.instr("#1") - # n = [[n, 0, m], "@rib_nil", 1] - # asm.comment(f"proc: {n}, ...") n = f"@{proc_label}" op = 4 From 0cc502c4da66c3541babf769866a319a28b20c35 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Tue, 13 Feb 2024 11:17:35 -0500 Subject: [PATCH 099/221] =?UTF-8?q?Remove=20=E2=80=9Cunimp=E2=80=9D=20tag?= =?UTF-8?q?=20from=20the=20halt=20primitive?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- alt/scheme/rvm.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 0f01b95..73efb8c 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -1549,9 +1549,13 @@ def return_from_primitive(): push("D") return_from_primitive() + asm.label("primitive_halt") asm.comment("primitive 21; halt :: -- (no more instructions are executed)") - unimp() + asm.instr("@halt_loop") + asm.instr("0;JMP") + asm.blank() + asm.label("primitive_unimp") asm.comment("Note: the current instr will be logged if tracing is enabled") @@ -1559,6 +1563,7 @@ def return_from_primitive(): asm.instr("0;JMP") asm.blank() + asm.label("interpreter_end") asm.blank() From be2dc183b51b54cfd752e87bffbe10155c09efd5 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Tue, 13 Feb 2024 13:41:49 -0500 Subject: [PATCH 100/221] Generate the full character map table --- alt/scheme/example/output.scm | 54 ++++++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/alt/scheme/example/output.scm b/alt/scheme/example/output.scm index 728b862..cf5b7f2 100644 --- a/alt/scheme/example/output.scm +++ b/alt/scheme/example/output.scm @@ -1,27 +1,59 @@ (define poke (rib 21 0 1)) -;; With a '*2' primitive, this wouldn't be such a terrible idea... -;; 16*5x = 16*(4*x + x) = 2*(2*(2*(2*(2*(2*x)) + x))) -(define (times2 x) (+ x x)) -(define (times80 x) (times2 (times2 (times2 (times2 - (+ (times2 (times2 x)) - x)))))) - (define screen 2048) -(define (drawchar x y c) (poke (+ screen (+ x (times80 y))) c)) +(define (drawchar x y c) (poke (+ screen (+ x (* 80 y))) c)) +;; First, ABCD in the corners of the screen: (drawchar 0 0 65) (drawchar 79 0 66) (drawchar 0 24 67) (drawchar 79 24 68) -;; About 40k cycles and 2% of the heap to get this far +;; About 40k cycles and 2% of the heap to get this far, without a primitive * +;; Down to 16K cycles and 0.8% of the heap using primitive * -;; TODO: character map table: +;; Now, a character map table: ;; 00 01 02 ... ;; ... ;; 30 0 1 2 ... ;; 40 @ A B ... -;; ... \ No newline at end of file +;; ... + +(define char-zero 48) +(define char-a 65) +(define (hex c) (if (< c 10) (+ c char-zero) (+ c (- char-a 10)))) + +;; Effectful loop: evaluate (f value) for each x <= value < y, discarding the result. +(define (for x y f) + (if (< x y) + (begin + (f x) + (for (+ x 1) y f)) + '())) + +;; Header row: 00 01 02 ... +(for 0 16 (lambda (x) + (begin + (drawchar (+ 5 (* 3 x)) 3 char-zero) + (drawchar (+ 6 (* 3 x)) 3 (hex x))))) + +;; About 200k cycles and 10% of the heap to this point + +;; Header column: 00, 10, ... 70 +(for 0 8 (lambda (y) + (begin + (drawchar 1 (+ y 4) (hex y)) + (drawchar 2 (+ y 4) char-zero)))) + +;; Fill in only the rows with printable chars: 2-7 +(for 2 8 (lambda (y) + (let ((h (* 16 y))) + (for 0 16 (lambda (x) + (drawchar + (+ 6 (* 3 x)) + (+ y 4) + (+ h x))))))) + +;; 1.1M cycles and 40% of the heap to complete From aea95135ac43b4e9fac762cdf6f33412cff935ab Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Tue, 13 Feb 2024 18:11:04 -0500 Subject: [PATCH 101/221] Add support for alternative memory layouts in codgen simulator --- alt/big.py | 12 ++++------- alt/test_big.py | 37 +++++++++++++++++++++++----------- nand/codegen.py | 53 +++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 71 insertions(+), 31 deletions(-) diff --git a/alt/big.py b/alt/big.py index c4fc33d..9512221 100755 --- a/alt/big.py +++ b/alt/big.py @@ -69,10 +69,6 @@ @chip def FlatMemory(inputs, outputs): """The same interface as MemorySystem, but also maps the ROM and extends address to the full 16 bits. - - FIXME: this will need some additional support in the "codegen" simulator, which otherwise implements - the standard MemorySystem directly. But that support will be useful to any Computer with different - memory layout. """ in_ = inputs.in_ @@ -317,7 +313,7 @@ def assemble(f, min_static=16, max_static=1023, builtins=BUILTIN_SYMBOLS): import time -def run(program, chip=BigComputer, name="Flat!", font="monaco-9", halt_addr=None, trace=None, verbose_tty=True): +def run(program, chip=BigComputer, simulator="codegen", name="Flat!", font="monaco-9", halt_addr=None, trace=None, verbose_tty=True): """Run with keyboard and text-mode graphics.""" # TODO: font @@ -328,7 +324,7 @@ def run(program, chip=BigComputer, name="Flat!", font="monaco-9", halt_addr=None elif any(b != 0 for b in program[:ROM_BASE]): print("WARNING: non-zero words found in the program image below ROM_BASE; this memory is hidden by low RAM") - computer = nand.syntax.run(chip, simulator="vector") + computer = nand.syntax.run(chip, simulator=simulator) computer.init_rom(program) @@ -437,7 +433,7 @@ def main(): import argparse parser = argparse.ArgumentParser(description="Run assembly source with text-mode display and keyboard") parser.add_argument("path", help="Path to source (.asm)") - # parser.add_argument("--simulator", action="store", default="codegen", help="One of 'vector' (slower, more precise); 'codegen' (faster, default); 'compiled' (experimental)") + parser.add_argument("--simulator", action="store", default="codegen", help="One of 'vector' (slower, more precise); 'codegen' (faster, default); 'compiled' (experimental)") # parser.add_argument("--trace", action="store_true", help="(VM/Jack-only) print cycle counts during initialization. Note: runs almost 3x slower.") # parser.add_argument("--print", action="store_true", help="(VM/Jack-only) print translated assembly.") # TODO: "--debug" showing opcode-level trace. Breakpoints, stepping, peek/poke? @@ -458,7 +454,7 @@ def main(): print(f"symbols: {symbols}") print(f"statics: {statics}") - run(program=prg, halt_addr=symbols["halt"]) + run(program=prg, simulator=args.simulator, halt_addr=symbols["halt"]) if __name__ == "__main__": diff --git a/alt/test_big.py b/alt/test_big.py index d0e4636..4572b5c 100755 --- a/alt/test_big.py +++ b/alt/test_big.py @@ -1,13 +1,23 @@ #! /usr/bin/env pytest +import pytest + from nand import run, gate_count import nand.component from alt.big import * -def test_memory_system(): - # FIXME: not handled by "codegen" yet - mem = nand.run(FlatMemory, simulator="vector") +# TODO: put this somewhere common: +def parameterize_simulators(f): + def vector(chip, **args): + return nand.run(chip, simulator="vector", **args) + def codegen(chip, **args): + return nand.run(chip, simulator="codegen", **args) + return pytest.mark.parametrize("run", [vector, codegen])(f) + +@parameterize_simulators +def test_memory_system(run): + mem = run(FlatMemory) # set RAM[0] = -1 mem.in_ = -1 @@ -113,8 +123,9 @@ def test_memory_system(): assert mem.out == -1 -def test_rom(): - mem = nand.run(FlatMemory, simulator="vector") +@parameterize_simulators +def test_rom(run): + mem = run(FlatMemory) # Writes to any ROM-mapped address are ignored: for addr in (ROM_BASE, ROM_BASE + 1234, ROM_BASE + 27*1024, HEAP_BASE - 1): @@ -154,9 +165,9 @@ def test_gates_mem(): assert gate_count(project_05.MemorySystem)['nands'] == 163 -def test_decode_alu(): - decode = nand.run(DecodeALU) - +@parameterize_simulators +def test_decode_alu(run): + decode = run(DecodeALU) # A typical computation with memory access: decode.instruction = parse_op("M=M-D") @@ -209,8 +220,9 @@ def test_gates_decode_alu(): assert gate_count(DecodeALU)['nands'] == 6 -def test_decode_jump(): - decode = nand.run(DecodeJump) +@parameterize_simulators +def test_decode_jump(run): + decode = run(DecodeJump) # A typical computation with memory access: @@ -435,8 +447,9 @@ def test_gates_cpu(): assert gate_count(project_05.CPU)['nands'] == 1099 -def test_computer_no_program(): - computer = nand.run(BigComputer) +@parameterize_simulators +def test_computer_no_program(run): + computer = run(BigComputer) for _ in range(100): computer.ticktock() diff --git a/nand/codegen.py b/nand/codegen.py index a7c701f..0cf7b66 100644 --- a/nand/codegen.py +++ b/nand/codegen.py @@ -13,13 +13,15 @@ - ROM: there should no more than one ROM present - MemorySystem: there should be no more than one MemorySystem present +If the conventional MemorySystem is not used, then these components can be used discretely: +- RAM, Input, Output: not more than one of each + A few more are implemented so that this simulator can also be used (and tested) with smaller chips: - DFF - DMux - DMux8Way - Mux8Way16 -- TODO: RAM, separately from MemorySystem Any other ICs that appear are flattened to combinations of these. The downside is that a moderate amount of flattening will have a significant impact on simulation speed. For example, @@ -36,7 +38,7 @@ the memory layout also entails constructing a new UI harness, which is beside the point. """ -from nand.component import Nand, Const, DFF, ROM +from nand.component import Nand, Const, DFF, ROM, RAM, Input, Output from nand.integration import IC, Connection, root, clock from nand.optimize import simplify from nand.vector import extend_sign @@ -131,10 +133,13 @@ def generate_python(ic, inline=True, prefix_super=False, cython=False): if any(conn == clock for conn in ic.wires.values()): raise NotImplementedError("This simulator cannot handle chips that refer to 'clock' directly.") - if any(isinstance(c, IC) and c.label == 'MemorySystem' for c in all_comps): + # if any(isinstance(c, IC) and c.label == 'MemorySystem' for c in all_comps): + if any(isinstance(c, ROM) for c in all_comps): supr = "SOC" + supr_args = ["15", "16"] # HACK: works for Big; doesn't break the standard CPU else: supr = "Chip" + supr_args = [] lines = [] def l(indent, str): @@ -237,6 +242,10 @@ def binary16(comp, template): return template.format(a=src_many(comp, 'a'), b=src_many(comp, 'b')) def component_expr(comp): + """Expression producing the value for a component's single output, or None if it can't be + represented that way. + """ + if isinstance(comp, Nand): return binary1(comp, "not ({a} and {b})") elif isinstance(comp, Const): @@ -300,6 +309,14 @@ def component_expr(comp): # the RAM? address = src_many(comp, 'address', 14) return f"self._ram[{address}] if 0 <= {address} < 0x4000 else (self._screen[{address} & 0x1fff] if 0x4000 <= {address} < 0x6000 else (self._keyboard if {address} == 0x6000 else 0))" + elif isinstance(comp, RAM): + address = src_many(comp, 'address', comp.address_bits) + return f"self._ram[{address}]" + elif isinstance(comp, Input): + return "self.input" + elif isinstance(comp, Output): + # FIXME: bogus? + return "self._tty == 0" else: raise Exception(f"Unrecognized primitive: {comp}") @@ -310,7 +327,7 @@ def component_expr(comp): l(0, f"class {class_name}({supr}):") l(1, f"def __init__(self):") - l(2, f"{supr}.__init__(self)") + l(2, f"{supr}.__init__({','.join(['self'] + supr_args)})") for name in ic.inputs(): l(2, f"self._{name} = 0 # input") for name in ic.outputs(): @@ -437,8 +454,21 @@ def component_expr(comp): l(6, f"self._tty = {in_name}") l(6, f"self._tty_ready = {in_name} != 0") any_state = True - elif isinstance(comp, (Const, ROM)): + elif isinstance(comp, (Const, ROM, Input)): pass + elif isinstance(comp, RAM): + address_expr = src_many(comp, 'address', 14) + in_name = f"_{all_comps.index(comp)}_in" + l(4, f"if {src_one(comp, 'load')}:") + l(5, f"{in_name} = {src_many(comp, 'in_')}") + l(5, f"self._ram[{address_expr}] = {in_name}") + any_state = True + elif isinstance(comp, Output): + in_name = f"_{all_comps.index(comp)}_in" + l(4, f"if {src_one(comp, 'load')}:") + l(5, f"self._tty = {in_name}") + l(5, f"self._tty_ready = {in_name} != 0") + any_state = True elif comp.label in PRIMITIVES: # All combinational components: nothing to do here pass @@ -499,10 +529,11 @@ def ticktock(self, cycles=1): class SOC(Chip): """Super for chips that include a full computer with ROM, RAM, keyboard input, and "TTY" output.""" - def __init__(self): - self._rom = [] - self._ram = [0]*(1 << 14) - self._screen = [0]*(1 << 13) + def __init__(self, rom_address_bits=15, ram_address_bits=14, screen_address_bits=13): + self._rom = [0]*(1 << rom_address_bits) + self._ram = [0]*(1 << ram_address_bits) + if screen_address_bits is not None: + self._screen = [0]*(1 << screen_address_bits) self._keyboard = 0 self._tty = 0 @@ -519,8 +550,8 @@ def init_rom(self, instructions): size = len(instructions) # Assuming a 15-bit ROM, as we do here, we can't address more than 32K of instructions: - if size >= 2**15: - raise Exception(f"Too many instructions: {size:0,d} >= {2**15:0,d}") + if size >= len(self._rom): + raise Exception(f"Too many instructions: {size:0,d} >= {len(self._rom):,d}") contents = instructions + [ size, # @size (which is the address of this instruction) From 539517fff5f8e381d55d88dccdc391dc32bbd030 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Tue, 13 Feb 2024 18:11:46 -0500 Subject: [PATCH 102/221] Add logic to avoid sending bogus addresses to the ROM --- alt/big.py | 2 +- alt/test_big.py | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/alt/big.py b/alt/big.py index 9512221..65c8455 100755 --- a/alt/big.py +++ b/alt/big.py @@ -87,7 +87,7 @@ def FlatMemory(inputs, outputs): is_io = Eq16(a=address, b=KEYBOARD_ADDR).out ram = RAM(16)(in_=in_, load=load, address=address) # Fully 64K; partially hidden by the ROM - rom = ROM(15)(address=address) # Based at 0 and sized to 32K, but partially hidden by RAM + rom = ROM(15)(address=Mux16(a=0, b=address, sel=is_rom).out) # Based at 0 and sized to 32K, but partially hidden by RAM # screen = RAM(10) # Separate RAM would make life easier for the harness? keyboard = Input() tty = Output(in_=in_, load=And(a=load, b=is_io).out) diff --git a/alt/test_big.py b/alt/test_big.py index 4572b5c..982cc4f 100755 --- a/alt/test_big.py +++ b/alt/test_big.py @@ -157,9 +157,13 @@ def test_gates_mem(): """Portion of the extra chip size that's in the memory system. This would have been extra chips on the board, not extra gates in the CPU, presumably. + + A surprising amount of this space (~50 gates) is just to avoid applying a out-of-range + address to the ROM, which the codegen simulator doesn't like. If that seems unfair, + the simulator could be modified to do the range-check. """ - assert gate_count(FlatMemory)['nands'] == 219 + assert gate_count(FlatMemory)['nands'] == 268 import project_05 assert gate_count(project_05.MemorySystem)['nands'] == 163 @@ -688,7 +692,7 @@ def test_gates_computer(): """Overall extra chip size.""" # Note: factoring out instruction decoding seems to have added 2 gates - assert gate_count(BigComputer)['nands'] == 1447 + assert gate_count(BigComputer)['nands'] == 1496 import project_05 assert gate_count(project_05.Computer)['nands'] == 1262 From 8d679a3a82f1fdfc03d5978da1f688c2f0abbcc0 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Tue, 13 Feb 2024 22:06:02 -0500 Subject: [PATCH 103/221] Move the range-check into the simulator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It’s just a pain to have to provide a list of the correct size, and it’s easy for the generated code to check anyway --- alt/big.py | 2 +- nand/codegen.py | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/alt/big.py b/alt/big.py index 65c8455..9512221 100755 --- a/alt/big.py +++ b/alt/big.py @@ -87,7 +87,7 @@ def FlatMemory(inputs, outputs): is_io = Eq16(a=address, b=KEYBOARD_ADDR).out ram = RAM(16)(in_=in_, load=load, address=address) # Fully 64K; partially hidden by the ROM - rom = ROM(15)(address=Mux16(a=0, b=address, sel=is_rom).out) # Based at 0 and sized to 32K, but partially hidden by RAM + rom = ROM(15)(address=address) # Based at 0 and sized to 32K, but partially hidden by RAM # screen = RAM(10) # Separate RAM would make life easier for the harness? keyboard = Input() tty = Output(in_=in_, load=And(a=load, b=is_io).out) diff --git a/nand/codegen.py b/nand/codegen.py index 0cf7b66..1b1a1f5 100644 --- a/nand/codegen.py +++ b/nand/codegen.py @@ -57,7 +57,7 @@ def translate(ic): class_name, lines = generate_python(ic) # print(ic) - # print_lines(lines) + print_lines(lines) eval(compile('\n'.join(lines), filename="", @@ -362,7 +362,12 @@ def component_expr(comp): pass elif isinstance(comp, ROM): # TODO: trap index errors with try/except - l(3, f"{output_name(comp)} = self._rom[{src_many(comp, 'address', comp.address_bits)}]") + address_name = f"_{all_comps.index(comp)}_address" + l(3, f"{address_name} = {src_many(comp, 'address', comp.address_bits)}") + l(3, f"if 0 <= {address_name} < len(self._rom):") + l(4, f"{output_name(comp)} = self._rom[{address_name}]") + l(3, "else:") + l(4, f"{output_name(comp)} = 0") elif comp.label == "DMux": in_name = f"_{all_comps.index(comp)}_in" sel_name = f"_{all_comps.index(comp)}_sel" @@ -595,6 +600,9 @@ def poke_screen(self, address, value): """Write a value to the display RAM. Address must be between 0x000 and 0x1FFF.""" self._screen[address] = extend_sign(value) + def peek_rom(self, address): + return self._rom[address] + def set_keydown(self, keycode): """Provide the code which identifies a single key which is currently pressed.""" self._keyboard = keycode From 4b06215cf37c0cf69bafbd2d7a9ebdda5ef2944e Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Tue, 13 Feb 2024 22:07:10 -0500 Subject: [PATCH 104/221] Revert the tests for range-checking the ROM in the simulator --- alt/test_big.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/alt/test_big.py b/alt/test_big.py index 982cc4f..4572b5c 100755 --- a/alt/test_big.py +++ b/alt/test_big.py @@ -157,13 +157,9 @@ def test_gates_mem(): """Portion of the extra chip size that's in the memory system. This would have been extra chips on the board, not extra gates in the CPU, presumably. - - A surprising amount of this space (~50 gates) is just to avoid applying a out-of-range - address to the ROM, which the codegen simulator doesn't like. If that seems unfair, - the simulator could be modified to do the range-check. """ - assert gate_count(FlatMemory)['nands'] == 268 + assert gate_count(FlatMemory)['nands'] == 219 import project_05 assert gate_count(project_05.MemorySystem)['nands'] == 163 @@ -692,7 +688,7 @@ def test_gates_computer(): """Overall extra chip size.""" # Note: factoring out instruction decoding seems to have added 2 gates - assert gate_count(BigComputer)['nands'] == 1496 + assert gate_count(BigComputer)['nands'] == 1447 import project_05 assert gate_count(project_05.Computer)['nands'] == 1262 From 9e84882ddfdcc69fc27318f952dd59e5344299ab Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Wed, 14 Feb 2024 09:36:52 -0500 Subject: [PATCH 105/221] Implement field0/1/2 --- alt/scheme/rvm.py | 34 +++++++++++++++++++++++++++--- alt/scheme/test_rvm.py | 48 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 3 deletions(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 73efb8c..03c2ba9 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -1380,15 +1380,43 @@ def return_from_primitive(): asm.label("primitive_field0") asm.comment("primitive 6; field0 :: rib(x, _, _) -- x") - unimp() + asm.comment("Update in place: SP.x.x = SP.x.x.x") + asm.instr("@SP") + asm.instr("A=M") + asm.instr("A=M") + asm.instr("D=M") + asm.instr("@SP") + asm.instr("A=M") + asm.instr("M=D") + return_from_primitive() + asm.label("primitive_field1") asm.comment("primitive 7; field1 :: rib(_, y, _) -- y") - unimp() + asm.comment("Update in place: SP.x.x = SP.x.x.y") + asm.instr("@SP") + asm.instr("A=M") + asm.instr("A=M+1") + asm.instr("D=M") + asm.instr("@SP") + asm.instr("A=M") + asm.instr("M=D") + return_from_primitive() + asm.label("primitive_field2") asm.comment("primitive 8; field2 :: rib(_, _, z) -- z") - unimp() + asm.comment("Update in place: SP.x.x = SP.x.x.z") + asm.instr("@SP") + asm.instr("A=M") + asm.instr("A=M+1") + asm.instr("A=A+1") + asm.instr("D=M") + asm.instr("@SP") + asm.instr("A=M") + asm.instr("M=D") + return_from_primitive() + asm.label("primitive_field0-set!") asm.comment("primitive 9; field0-set! :: rib(_, y, z) x -- x (and update the rib in place: rib(x, y, z))") diff --git a/alt/scheme/test_rvm.py b/alt/scheme/test_rvm.py index d4545dc..c2857ba 100755 --- a/alt/scheme/test_rvm.py +++ b/alt/scheme/test_rvm.py @@ -169,6 +169,54 @@ def test_tty(): +# +# Tests for specific primitives: +# + +def test_field0(): + program = """ + (define field0 (rib 6 0 1)) + + (field0 '(7 8 9)) + """ + + inspect, output = run_to_halt(program, max_cycles=20000) + + assert inspect.stack() == [7] # car + assert output == [] + + +def test_field1(): + program = """ + (define field1 (rib 7 0 1)) + + (field1 '(7 8 9)) + """ + + inspect, output = run_to_halt(program, max_cycles=20000) + + assert inspect.stack() == [[8, 9]] # cdr + assert output == [] + + +def test_field2(): + program = """ + (define field2 (rib 8 0 1)) + + (field2 '(7 8 9)) + """ + + inspect, output = run_to_halt(program, max_cycles=20000) + + assert inspect.stack() == [0] # pair-type + assert output == [] + + + +# +# Helpers +# + def several(*exprs): """Make a program that evaluates several expressions and constructs a list with each result.""" def go(xs): From e47a018a5370ae9ffff04cd06f1489900a0c8d5b Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Wed, 14 Feb 2024 09:45:00 -0500 Subject: [PATCH 106/221] Run tests on both simulators --- alt/scheme/rvm.py | 11 +++-- alt/scheme/test_rvm.py | 104 +++++++++++++++++++++++++---------------- 2 files changed, 71 insertions(+), 44 deletions(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 03c2ba9..99c9c79 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -33,16 +33,16 @@ DEFAULT_TRACE_LEVEL = TRACE_FINE -def run(program, print_asm=DEFAULT_PRINT_ASM, trace_level=DEFAULT_TRACE_LEVEL, verbose_tty=True): +def run(program, simulator, print_asm=DEFAULT_PRINT_ASM, trace_level=DEFAULT_TRACE_LEVEL, verbose_tty=True): encoded = compile(program) if print_asm: print(f"encoded program: {repr(encoded)}") - run_compiled(encoded, print_asm, trace_level, verbose_tty) + run_compiled(encoded, simulator, print_asm, trace_level, verbose_tty) -def run_compiled(encoded, print_asm=DEFAULT_PRINT_ASM, trace_level=DEFAULT_TRACE_LEVEL, verbose_tty=True): +def run_compiled(encoded, simulator, print_asm=DEFAULT_PRINT_ASM, trace_level=DEFAULT_TRACE_LEVEL, verbose_tty=True): asm = AssemblySource() @@ -122,6 +122,7 @@ def trace(computer, cycles): print(f"{cycles:3,d}: {computer.pc}") big.run(program=instrs, + simulator=simulator, name="Scheme", halt_addr=symbols["halt_loop"], trace=trace if trace_level > TRACE_NONE else None, @@ -1600,12 +1601,12 @@ def main(): import sys # TODO: command-line args, multiple source files, etc. - # --print, --trace + # --print, --trace, --simulator with open(sys.argv[1]) as f: program = "".join(f.readlines()) - run(program, print_asm=True, trace_level=TRACE_COARSE) + run(program, simulator="codegen", print_asm=False, trace_level=TRACE_NONE) if __name__ == "__main__": diff --git a/alt/scheme/test_rvm.py b/alt/scheme/test_rvm.py index c2857ba..2bce183 100755 --- a/alt/scheme/test_rvm.py +++ b/alt/scheme/test_rvm.py @@ -9,113 +9,134 @@ from nand.translate import AssemblySource from nand.vector import unsigned -def test_trivial(): +# TODO: put this somewhere common: +def parameterize_simulators(f): + def vector(chip, **args): + return run_to_halt(chip, simulator="vector", **args) + def codegen(chip, **args): + return run_to_halt(chip, simulator="codegen", **args) + return pytest.mark.parametrize("run", [vector, codegen])(f) + + +@parameterize_simulators +def test_trivial(run): program = "42" - inspect, output = run_to_halt(program, max_cycles=20000) + inspect, output = run(program) assert inspect.stack() == [42] assert output == [] -def test_string(): +@parameterize_simulators +def test_string(run): program = '"abc"' - inspect, output = run_to_halt(program, max_cycles=20000) + inspect, output = run(program) assert inspect.stack() == ["abc"] assert output == [] -def test_lt(): +@parameterize_simulators +def test_lt(run): program = several("(< 1 2)", "(< 1 1)", "(< 2 1)") - inspect, output = run_to_halt(program, max_cycles=20000) + inspect, output = run(program) assert inspect.stack() == [[True, False, False]] assert output == [] -def test_add(): +@parameterize_simulators +def test_add(run): program = "(+ 1 2)" - inspect, output = run_to_halt(program, max_cycles=20000) + inspect, output = run(program) assert inspect.stack() == [3] assert output == [] -def test_sub(): +@parameterize_simulators +def test_sub(run): program = "(- 123 234)" - inspect, output = run_to_halt(program, max_cycles=20000) + inspect, output = run(program) assert inspect.stack() == [-111] assert output == [] -def test_mul(): +@parameterize_simulators +def test_mul(run): program = "(* 6 7)" - inspect, output = run_to_halt(program, max_cycles=20000) + inspect, output = run(program) assert inspect.stack() == [42] assert output == [] -def test_mul_mixed_signs(): +@parameterize_simulators +def test_mul_mixed_signs(run): program = several("(* 6 -7)", "(* -14 -3)", "(* 2 -21)") - inspect, output = run_to_halt(program, max_cycles=20000) + inspect, output = run(program) assert inspect.stack() == [[-42, 42, -42]] assert output == [] -def test_if(): +@parameterize_simulators +def test_if(run): program = "(if #t 42 '())" - inspect, output = run_to_halt(program, max_cycles=20000) + inspect, output = run(program) assert inspect.stack() == [42] assert output == [] -def test_quote(): +@parameterize_simulators +def test_quote(run): program = "'()" - inspect, output = run_to_halt(program, max_cycles=20000) + inspect, output = run(program) assert inspect.stack() == [[]] assert output == [] -def test_lambda(): +@parameterize_simulators +def test_lambda(run): program = """ ((lambda (x y) (+ x y)) 14 28) """ - inspect, output = run_to_halt(program, max_cycles=20000) + inspect, output = run(program) assert inspect.stack() == [42] assert output == [] -def test_define(): +@parameterize_simulators +def test_define(run): program = """ (define (cons x y) (rib x y 0)) (cons 1 '(2)) """ - inspect, output = run_to_halt(program, max_cycles=20000) + inspect, output = run(program) assert inspect.stack() == [[1, 2]] assert output == [] -def test_fact(): +@parameterize_simulators +def test_fact(run): program = """ (define (fact n) (if (< n 2) 1 @@ -124,45 +145,48 @@ def test_fact(): (fact 5) """ - inspect, output = run_to_halt(program, max_cycles=20000) + inspect, output = run(program) assert inspect.stack() == [120] assert output == [] -def test_capture(): +@parameterize_simulators +def test_capture(run): program = """ (define (add x) (lambda (y) (+ x y))) ((add 1) 2) """ - inspect, output = run_to_halt(program, max_cycles=20000) + inspect, output = run(program) assert inspect.stack() == [3] assert output == [] -def test_draw(): +@parameterize_simulators +def test_draw(run): program = """ (define poke (rib 21 0 1)) (define screen 1024) (poke screen 65) """ - inspect, output = run_to_halt(program, max_cycles=20000) + inspect, output = run(program) assert inspect.stack() == [65] assert output == [] -def test_tty(): +@parameterize_simulators +def test_tty(run): program = """ (define poke (rib 21 0 1)) (define keyboard 4095) (poke keyboard 48) """ - inspect, output = run_to_halt(program, max_cycles=20000) + inspect, output = run(program) assert inspect.stack() == [48] assert output == [ord('0')] @@ -173,40 +197,42 @@ def test_tty(): # Tests for specific primitives: # -def test_field0(): +@parameterize_simulators +def test_field0(run): program = """ (define field0 (rib 6 0 1)) (field0 '(7 8 9)) """ - inspect, output = run_to_halt(program, max_cycles=20000) + inspect, output = run(program) assert inspect.stack() == [7] # car assert output == [] -def test_field1(): +@parameterize_simulators +def test_field1(run): program = """ (define field1 (rib 7 0 1)) (field1 '(7 8 9)) """ - inspect, output = run_to_halt(program, max_cycles=20000) + inspect, output = run(program) assert inspect.stack() == [[8, 9]] # cdr assert output == [] - -def test_field2(): +@parameterize_simulators +def test_field2(run): program = """ (define field2 (rib 8 0 1)) (field2 '(7 8 9)) """ - inspect, output = run_to_halt(program, max_cycles=20000) + inspect, output = run(program) assert inspect.stack() == [0] # pair-type assert output == [] @@ -228,7 +254,7 @@ def go(xs): return "(define (cons x y) (rib x y 0))\n" + go(list(exprs)) -def run_to_halt(program, max_cycles=5000): +def run_to_halt(program, max_cycles=20000, simulator="codegen"): """Compile and run a Scheme program, then return a function for inspecting the RAM, and a list of words that were written to the TTY port. """ @@ -244,7 +270,7 @@ def run_to_halt(program, max_cycles=5000): instrs, symbols, _ = big.assemble(asm.lines, min_static=None, builtins=rvm.BUILTINS) - computer = nand.syntax.run(big.BigComputer, simulator="vector") + computer = nand.syntax.run(big.BigComputer, simulator=simulator) computer.init_rom(instrs) From 622a512a29c9d48184f87683879cee7c05d8dc84 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Wed, 14 Feb 2024 09:45:54 -0500 Subject: [PATCH 107/221] Dial back the update frequency a bit to keep up with the faster simulator --- alt/big.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alt/big.py b/alt/big.py index 9512221..3309fd0 100755 --- a/alt/big.py +++ b/alt/big.py @@ -362,7 +362,7 @@ def run(program, chip=BigComputer, simulator="codegen", name="Flat!", font="mona if trace is not None: - CYCLES_PER_UPDATE = 2 # HACK: to ensure we see all the TTY output + CYCLES_PER_UPDATE = 20 # HACK: to ensure we see all the TTY output else: CYCLES_PER_UPDATE = 100 From c0d07a6d8444f8f707707521dc1f9253b710ba73 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Wed, 14 Feb 2024 09:49:15 -0500 Subject: [PATCH 108/221] =?UTF-8?q?Run=20the=20actual=20Ribbit=20repl=20(w?= =?UTF-8?q?hich=20isn=E2=80=99t=20working=20yet)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- alt/scheme/repl.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/alt/scheme/repl.py b/alt/scheme/repl.py index d75b0c2..d62a78b 100755 --- a/alt/scheme/repl.py +++ b/alt/scheme/repl.py @@ -9,6 +9,22 @@ from alt.scheme import rvm def main(): + with open("alt/scheme/ribbit/min.scm") as f: + min_library_src_lines = f.readlines() + + # TODO: need to reference the library functions we want to be able available + program = "".join(min_library_src_lines) + "\n\n(repl)" + + # Note: actually running the compiler in the Ribbit Python interpreter is pretty slow. + # Probably want to cache the encoded result somewhere (or just go back to hard-coding it here.) + print("Compiling...") + + rvm.run(program, simulator="codegen", print_asm=False, trace_level=rvm.TRACE_COARSE) + + +def temp(): + """Simpler examples for now.""" + # pgm = """ # ;; In lieu of pre-defining additional primitives and re-building rsc, # ;; with the same result: @@ -28,3 +44,4 @@ def main(): if __name__ == "__main__": main() + # temp() From ea4b9ebb96d4c7c940050c778284fa9c631a0641 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Wed, 14 Feb 2024 10:12:47 -0500 Subject: [PATCH 109/221] Implement field0/1/2-set! --- alt/scheme/rvm.py | 49 +++++++++++++++++++++++++++++++++++--- alt/scheme/test_rvm.py | 54 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 3 deletions(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 99c9c79..4a39493 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -1421,15 +1421,58 @@ def return_from_primitive(): asm.label("primitive_field0-set!") asm.comment("primitive 9; field0-set! :: rib(_, y, z) x -- x (and update the rib in place: rib(x, y, z))") - unimp() + asm.comment("TEMP_0 = pop() = x") + pop("TEMP_0") + asm.instr("@TEMP_0") + asm.instr("D=M") + asm.comment("Update the rib in place: SP.x.x = x") + asm.instr("@SP") + asm.instr("A=M") + asm.instr("A=M") + asm.instr("M=D") + asm.comment("Update the top stack entry in place") + asm.instr("@SP") + asm.instr("A=M") + asm.instr("M=D") + return_from_primitive() + asm.label("primitive_field1-set!") asm.comment("primitive 10; field1-set! :: rib(x, _, z) y -- y (and update the rib in place: rib(x, y, z))") - unimp() + asm.comment("TEMP_0 = pop() = y") + pop("TEMP_0") + asm.instr("@TEMP_0") + asm.instr("D=M") + asm.comment("Update the rib in place: SP.x.y = y") + asm.instr("@SP") + asm.instr("A=M") + asm.instr("A=M+1") + asm.instr("M=D") + asm.comment("Update the top stack entry in place") + asm.instr("@SP") + asm.instr("A=M") + asm.instr("M=D") + return_from_primitive() + asm.label("primitive_field2-set!") asm.comment("primitive 11; field2-set! :: rib(x, y, _) z -- z (and update the rib in place: rib(x, y, z))") - unimp() + asm.comment("TEMP_0 = pop() = x") + pop("TEMP_0") + asm.instr("@TEMP_0") + asm.instr("D=M") + asm.comment("Update the rib in place: SP.x.x = x") + asm.instr("@SP") + asm.instr("A=M") + asm.instr("A=M+1") + asm.instr("A=A+1") + asm.instr("M=D") + asm.comment("Update the top stack entry in place") + asm.instr("@SP") + asm.instr("A=M") + asm.instr("M=D") + return_from_primitive() + asm.label("primitive_eqv?") asm.comment("primitive 12; eqv? :: x y -- bool(x is identical to y)") diff --git a/alt/scheme/test_rvm.py b/alt/scheme/test_rvm.py index 2bce183..5e93e9b 100755 --- a/alt/scheme/test_rvm.py +++ b/alt/scheme/test_rvm.py @@ -238,6 +238,60 @@ def test_field2(run): assert output == [] +@parameterize_simulators +def test_field0_set(run): + program = """ + (define field0-set! (rib 9 0 1)) + (define (pair x y) (rib x y 0)) + + (define foo '(7 8 9)) + + ;; Leave both the modified-in-place cons cell and the value on the stack for inspection: + (pair foo (field0-set! foo 10)) + """ + + inspect, output = run(program) + + assert inspect.stack() == [([10, 8, 9], 10)] + assert output == [] + + +@parameterize_simulators +def test_field1_set(run): + program = """ + (define field1-set! (rib 10 0 1)) + (define (pair x y) (rib x y 0)) + + (define foo '(7 8 9)) + + ;; Leave both the modified-in-place cons cell and the value on the stack for inspection: + (pair foo (field1-set! foo '(10 11))) + """ + + inspect, output = run(program) + + # Note: here the cdr is acually a list, so it gets interpreted that way + assert inspect.stack() == [[[7, 10, 11], 10, 11]] + assert output == [] + + +@parameterize_simulators +def test_field2_set(run): + program = """ + (define field2-set! (rib 11 0 1)) + (define (pair x y) (rib x y 0)) + + (define foo '(7 8 9)) + + ;; Hard to make a legit value by changing the type, so just update it and then pull it back out + (pair (field2-set! foo 11) (field2 foo)) + """ + + inspect, output = run(program) + + assert inspect.stack() == [(11, 11)] + assert output == [] + # # Helpers From 47bb7bdb85a89dea68a6d15a3cae91af75cebdc3 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Wed, 14 Feb 2024 10:20:23 -0500 Subject: [PATCH 110/221] Define a simple putchar in scheme --- alt/scheme/repl.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/alt/scheme/repl.py b/alt/scheme/repl.py index d62a78b..998fc75 100755 --- a/alt/scheme/repl.py +++ b/alt/scheme/repl.py @@ -13,7 +13,22 @@ def main(): min_library_src_lines = f.readlines() # TODO: need to reference the library functions we want to be able available - program = "".join(min_library_src_lines) + "\n\n(repl)" + program = "".join(min_library_src_lines) + """ + + (define poke (rib 21 0 1)) + + (define screen 2048) + (define (drawchar x y c) (poke (+ screen (+ x (* 80 y))) c)) + + (define cursorx 0) + (define cursory 0) + (define (putchar c) + (begin + (drawchar cursorx cursory c) + (set! cursorx (+ 1 cursorx)))) + + (repl) + """ # Note: actually running the compiler in the Ribbit Python interpreter is pretty slow. # Probably want to cache the encoded result somewhere (or just go back to hard-coding it here.) From 9e29d8e3d1cba3062114ce6cdeab6c0321e4a548 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Wed, 14 Feb 2024 10:48:22 -0500 Subject: [PATCH 111/221] Implement eqv? --- alt/scheme/rvm.py | 30 +++++++++++++++++++++++++++++- alt/scheme/test_rvm.py | 36 +++++++++++++++++++++++++++++++++++- 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 4a39493..45e66de 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -1467,6 +1467,7 @@ def return_from_primitive(): asm.instr("A=M+1") asm.instr("A=A+1") asm.instr("M=D") + # FIXME: jump to shared copy of this common sequence asm.comment("Update the top stack entry in place") asm.instr("@SP") asm.instr("A=M") @@ -1476,7 +1477,34 @@ def return_from_primitive(): asm.label("primitive_eqv?") asm.comment("primitive 12; eqv? :: x y -- bool(x is identical to y)") - unimp() + asm.comment("TEMP_0 = pop() = y") + pop("TEMP_0") + asm.instr("@TEMP_0") + asm.instr("D=M") + asm.instr("@SP") + asm.instr("A=M") + asm.instr("D=D-M") + eqv_true_label = asm.next_label("eqv_true") + asm.instr(f"@{eqv_true_label}") + asm.instr("D;JEQ") + asm.instr("@rib_false") + asm.instr("D=A") + # FIXME: jump to shared copy of this common sequence + asm.comment("Update the top stack entry in place") + asm.instr("@SP") + asm.instr("A=M") + asm.instr("M=D") + return_from_primitive() + asm.label(eqv_true_label) + asm.instr("@rib_true") + asm.instr("D=A") + # FIXME: jump to shared copy of this common sequence + asm.comment("Update the top stack entry in place") + asm.instr("@SP") + asm.instr("A=M") + asm.instr("M=D") + return_from_primitive() + asm.label("primitive_<") asm.comment("primitive 13; < :: x y -- bool(x < y)") diff --git a/alt/scheme/test_rvm.py b/alt/scheme/test_rvm.py index 5e93e9b..04a9033 100755 --- a/alt/scheme/test_rvm.py +++ b/alt/scheme/test_rvm.py @@ -293,6 +293,40 @@ def test_field2_set(run): assert output == [] +@parameterize_simulators +def test_eqv_simple(run): + program = """ + (define eqv? (rib 12 0 1)) + + (define x 42) + (define y 42) ;; same value means same object + (define z 11) + + """ + several("(eqv? x x)", "(eqv? x y)", "(eqv? x z)") + + inspect, output = run(program) + + assert inspect.stack() == [[True, True, False]] + assert output == [] + + +@parameterize_simulators +def test_eqv_ribs(run): + program = """ + (define eqv? (rib 12 0 1)) + + (define x '(1 2 3)) + (define y '(1 2 3)) ;; not the same object, despite same contents + (define z '(3 4 5)) + + """ + several("(eqv? x x)", "(eqv? x y)", "(eqv? x z)") + + inspect, output = run(program) + + assert inspect.stack() == [[True, False, False]] + assert output == [] + + # # Helpers # @@ -305,7 +339,7 @@ def go(xs): return " '()" else: return f"(cons {xs[0]}\n{go(xs[1:])})" - return "(define (cons x y) (rib x y 0))\n" + go(list(exprs)) + return "(define (cons $$x $$y) (rib $$x $$y 0))\n" + go(list(exprs)) def run_to_halt(program, max_cycles=20000, simulator="codegen"): From c27df7e1d70f030d18bfd060215dcf70924ab285 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Wed, 14 Feb 2024 17:52:47 -0500 Subject: [PATCH 112/221] Fix TTY wiring for alternative memory system --- nand/codegen.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nand/codegen.py b/nand/codegen.py index 1b1a1f5..2e61444 100644 --- a/nand/codegen.py +++ b/nand/codegen.py @@ -313,7 +313,7 @@ def component_expr(comp): address = src_many(comp, 'address', comp.address_bits) return f"self._ram[{address}]" elif isinstance(comp, Input): - return "self.input" + return "self._keyboard" elif isinstance(comp, Output): # FIXME: bogus? return "self._tty == 0" @@ -471,6 +471,7 @@ def component_expr(comp): elif isinstance(comp, Output): in_name = f"_{all_comps.index(comp)}_in" l(4, f"if {src_one(comp, 'load')}:") + l(5, f"{in_name} = {src_many(comp, 'in_')}") l(5, f"self._tty = {in_name}") l(5, f"self._tty_ready = {in_name} != 0") any_state = True From b3b918c359fecd1e84d8cde93ceb73343a4a786b Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Wed, 14 Feb 2024 18:15:53 -0500 Subject: [PATCH 113/221] Implement getchar (low-level, just reading the memory) --- alt/scheme/rvm.py | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 45e66de..4f98fe6 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -1283,8 +1283,8 @@ def wrangle_closure_params(): asm.instr("@primitive_*") asm.comment("17 (quotient: not implemented)") asm.instr("@primitive_unimp") - asm.comment("18 (getchar: not implemented)") - asm.instr("@primitive_unimp") + asm.comment("18") + asm.instr("@primitive_getchar") asm.comment("19 (putchar: not implemented)") asm.instr("@primitive_unimp") asm.comment("20") @@ -1631,10 +1631,31 @@ def return_from_primitive(): return_from_primitive() + asm.label("primitive_getchar") + asm.comment("primitive 18: getchar :: -- (blocks until a key is pressed)") + # Note: this will only catch keypresses that occur after the instruction is executed. For + # a responsive shell, the check will have to incorporated into the interpreter loop. + # It might even need to be be checked more often than once per instruction, since instructions + # can take as long as hundreds of cycles. + asm.comment("Loop until @KEYBOARD contains anything other than zero.") + + getchar_loop_label = asm.next_label("getchar_loop") + + asm.label(getchar_loop_label) + asm.instr("@KEYBOARD") + asm.instr("D=M") + asm.instr(f"@{getchar_loop_label}") + asm.instr("D;JEQ") + + push("D") + return_from_primitive() + + asm.label("primitive_peek") asm.comment("primitive 19; peek :: x -- RAM[x]") unimp() + asm.label("primitive_poke") asm.comment("primitive 20; poke :: x y -- y (and write the value y at RAM[x])") asm.comment("R5 = value") From 39f8f90286a6f6644f91625a3d58d81683b63c62 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Wed, 14 Feb 2024 18:16:04 -0500 Subject: [PATCH 114/221] Implement arg2 --- alt/scheme/rvm.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 4f98fe6..1290f15 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -1332,23 +1332,38 @@ def return_from_primitive(): push("D") return_from_primitive() + asm.label("primitive_id") asm.comment("primitive 1; id :: x -- x") asm.comment("... and, that's all folks") return_from_primitive() + asm.label("primitive_arg1") asm.comment("primitive 2; arg1 :: x y -- x") # i.e. "drop" + asm.comment("Simply discard the top entry on the stack by updating SP") asm.instr("@SP") - asm.instr("A=M+1") # addr of top entry.y - asm.instr("D=M") # addr of next entry + asm.instr("A=M+1") + asm.instr("D=M") asm.instr("@SP") asm.instr("M=D") return_from_primitive() + asm.label("primitive_arg2") asm.comment("primitive 3; arg2 :: x y -- y") - unimp() + asm.comment("Discard the second entry on the stack by updating the top entry") + asm.comment("D = the addr of the third entry from the top of the stack") + asm.instr("@SP") + asm.instr("A=M+1") + asm.instr("A=M+1") + asm.instr("D=M") + asm.comment("SP.x = D") + asm.instr("@SP") + asm.instr("A=M+1") + asm.instr("M=D") + return_from_primitive() + asm.label("primitive_close") asm.comment("primitive 4; close :: x -- rib(x[0], stack, 1)") From 241aa3d15fe68787c7f744595c2215e541b815fa Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Wed, 14 Feb 2024 18:55:03 -0500 Subject: [PATCH 115/221] Implement rib? primitive --- alt/scheme/rvm.py | 39 ++++++++++++++++++++++++++++++++++++++- alt/scheme/test_rvm.py | 14 ++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 1290f15..ba50ec2 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -1390,9 +1390,46 @@ def return_from_primitive(): asm.instr("M=D") return_from_primitive() + asm.label("primitive_rib?") asm.comment("primitive 5; rib? :: x -- bool(x is a rib)") - unimp() + # FIXME: this test is super bogus. Need to implement tagged ints, and test the correct bit here. + # For now, any value large enough to be a potential address + + is_rib_nonneg_label = asm.next_label("is_rib_nonneg") + is_rib_true_label = asm.next_label("is_rib_true") + + asm.instr("@SP") + asm.instr("A=M") + asm.instr("D=M") + asm.instr(f"@{is_rib_nonneg_label}") + asm.instr("D;JGE") + asm.instr("D=-D") + asm.label(is_rib_nonneg_label) + asm.comment("If the (absolute) value is larger than the base address of the ROM, assume it's a rib address") + asm.instr("@ROM") + asm.instr("D=D-A") + asm.instr(f"@{is_rib_true_label}") + asm.instr("D;JGT") + + asm.instr("@rib_false") + asm.instr("D=A") + # FIXME: jump to shared copy of this common sequence + asm.comment("Update the top stack entry in place") + asm.instr("@SP") + asm.instr("A=M") + asm.instr("M=D") + return_from_primitive() + asm.label(is_rib_true_label) + asm.instr("@rib_true") + asm.instr("D=A") + # FIXME: jump to shared copy of this common sequence + asm.comment("Update the top stack entry in place") + asm.instr("@SP") + asm.instr("A=M") + asm.instr("M=D") + return_from_primitive() + asm.label("primitive_field0") asm.comment("primitive 6; field0 :: rib(x, _, _) -- x") diff --git a/alt/scheme/test_rvm.py b/alt/scheme/test_rvm.py index 04a9033..9df7b07 100755 --- a/alt/scheme/test_rvm.py +++ b/alt/scheme/test_rvm.py @@ -327,6 +327,19 @@ def test_eqv_ribs(run): assert output == [] +@parameterize_simulators +def test_ribq(run): + program = """ + (define rib? (rib 5 0 1)) + + """ + several("(rib? 123)", "(rib? -345)", "(rib? #t)", '(rib? "abc")') + + inspect, output = run(program) + + assert inspect.stack() == [[False, False, True, True]] + assert output == [] + + # # Helpers # @@ -377,6 +390,7 @@ def run_to_halt(program, max_cycles=20000, simulator="codegen"): tty_char = computer.get_tty() if tty_char: + print(f"tty: {tty_char}") output.append(tty_char) if computer.fetch and inspect.is_labeled(computer.pc): From 2555da827c0038557155262bc1769d65dee1d45d Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Thu, 15 Feb 2024 09:07:19 -0500 Subject: [PATCH 116/221] Parse command-line args --- alt/scheme/rvm.py | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index ba50ec2..d5b67cc 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -1742,15 +1742,24 @@ def return_from_primitive(): def main(): - import sys - - # TODO: command-line args, multiple source files, etc. - # --print, --trace, --simulator - - with open(sys.argv[1]) as f: - program = "".join(f.readlines()) - - run(program, simulator="codegen", print_asm=False, trace_level=TRACE_NONE) + import argparse + parser = argparse.ArgumentParser(description="Run Scheme source with text-mode display and keyboard") + parser.add_argument("path", nargs=argparse.ONE_OR_MORE, help="Path to source (.scm)") + parser.add_argument("--simulator", action="store", default="codegen", help="One of 'vector' (slower, more precise); 'codegen' (faster, default); 'compiled' (experimental)") + parser.add_argument("--trace", action="store_true", help="Print each Ribbit instruction as it is interpreted. Note: runs almost 3x slower.") + parser.add_argument("--print", action="store_true", help="Print interpreter assembly and compiled instructions.") + + args = parser.parse_args() + + src_lines = [] + for p in args.path: + with open(p) as f: + src_lines += [] + f.readlines() + + run("".join(src_lines), + simulator=args.simulator, + print_asm=args.print, + trace_level=TRACE_COARSE if args.trace else TRACE_NONE) if __name__ == "__main__": From 5bce57dcd74e3decf881c5712e72c8ef2973295f Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Thu, 15 Feb 2024 09:07:48 -0500 Subject: [PATCH 117/221] Suppress printing of generated code --- nand/codegen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nand/codegen.py b/nand/codegen.py index 2e61444..eb06143 100644 --- a/nand/codegen.py +++ b/nand/codegen.py @@ -57,7 +57,7 @@ def translate(ic): class_name, lines = generate_python(ic) # print(ic) - print_lines(lines) + # print_lines(lines) eval(compile('\n'.join(lines), filename="", From 59ab486dbea3e3eee9296ce3d37928c736b2d231 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Fri, 16 Feb 2024 12:16:23 -0500 Subject: [PATCH 118/221] Use predictable names for statics --- alt/reg.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/alt/reg.py b/alt/reg.py index 095ba34..2ef66dd 100755 --- a/alt/reg.py +++ b/alt/reg.py @@ -1150,12 +1150,13 @@ def handle_Store(self, ast: Store): kind, index = ast.location.kind, ast.location.idx if kind == "static": + symbol_name = f"{self.class_namespace}.static_{ast.name}" if imm is not None: - self.asm.instr(f"@{self.class_namespace}.static{index}") + self.asm.instr(f"@{symbol_name}") self.asm.instr(f"M={imm}") else: self._handle(ast.value) - self.asm.instr(f"@{self.class_namespace}.static{index}") + self.asm.instr(f"@{symbol_name}") self.asm.instr("M=D") elif kind == "field": @@ -1386,7 +1387,8 @@ def handle_Const(self, ast: Const): def handle_Location(self, ast: Location): kind, index = ast.kind, ast.idx if ast.kind == "static": - self.asm.instr(f"@{self.class_namespace}.static{ast.idx}") + symbol_name = f"{self.class_namespace}.static_{ast.name}" + self.asm.instr(f"@{symbol_name}") self.asm.instr("D=M") elif ast.kind == "field": raise Exception(f"should have been rewritten: {ast}") From 88a942dd8a484f33a0546b03022179e832e0c865 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Fri, 16 Feb 2024 12:17:05 -0500 Subject: [PATCH 119/221] Accept a pre-configured AssemblySource --- alt/reg.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/alt/reg.py b/alt/reg.py index 2ef66dd..86666a3 100755 --- a/alt/reg.py +++ b/alt/reg.py @@ -1000,8 +1000,8 @@ def compile_class(ast: jack_ast.Class) -> Class: RESULT = "R12" # for now, just use one of the registers also used for local variables. class Translator(solved_07.Translator): - def __init__(self): - self.asm = AssemblySource() + def __init__(self, asm=None): + self.asm = asm if asm else AssemblySource() solved_07.Translator.__init__(self, self.asm) # self.preamble() # called by the loader, apparently From 6e36366b0dc436f95e82a654b54ef2c6659c2014 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Fri, 16 Feb 2024 12:17:34 -0500 Subject: [PATCH 120/221] Note on out-of bounds constant values --- alt/big.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/alt/big.py b/alt/big.py index 3309fd0..17da095 100755 --- a/alt/big.py +++ b/alt/big.py @@ -58,8 +58,8 @@ SCREEN_BASE = 0x0800 KEYBOARD_ADDR = 0x0FFF ROM_BASE = 0x1000 -HEAP_BASE = 0x8000 -HEAP_TOP = 0xFFFF +HEAP_BASE = 0x8000 # Note: too big for "@-" instruction +HEAP_TOP = 0xFFFF # Note: too big for "@-" instruction # From a32ac5bef87d8b12eb198c04a76d0d3e59e05237 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Fri, 16 Feb 2024 12:17:50 -0500 Subject: [PATCH 121/221] Update getchar comment --- alt/scheme/inspector.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alt/scheme/inspector.py b/alt/scheme/inspector.py index 69311e0..4b58671 100644 --- a/alt/scheme/inspector.py +++ b/alt/scheme/inspector.py @@ -170,7 +170,7 @@ def show_stack(self): 15: "-", 16: "*", 17: "quotient (unimp.)", - 18: "getchar (unimp.)", + 18: "getchar", 19: "putchar (unimp.)", # Extra: 20: "peek", From 456a075c392b6d4f15af2cc127e4ec7e9c83119a Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Fri, 16 Feb 2024 12:19:02 -0500 Subject: [PATCH 122/221] Constants to enable useful logging for debug --- nand/codegen.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/nand/codegen.py b/nand/codegen.py index eb06143..59956e0 100644 --- a/nand/codegen.py +++ b/nand/codegen.py @@ -44,6 +44,10 @@ from nand.vector import extend_sign +# For debugging: +PRINT_FLATTENED = False +PRINT_GENERATED = False + def run(ic): """Prepare an IC for simulation, returning an object which exposes the inputs and outputs as attributes. If the IC is Computer, it also provides access to the ROM, RAM, etc. @@ -57,7 +61,8 @@ def translate(ic): class_name, lines = generate_python(ic) # print(ic) - # print_lines(lines) + if PRINT_GENERATED: + print_lines(lines) eval(compile('\n'.join(lines), filename="", @@ -126,7 +131,8 @@ def generate_python(ic, inline=True, prefix_super=False, cython=False): ic = ic.flatten(primitives=PRIMITIVES) # ic = simplify(ic.flatten(primitives=PRIMITIVES)) # TODO: don't flatten everything in simplify - # print(ic) + if PRINT_FLATTENED: + print(ic) all_comps = ic.sorted_components() From e394c3bd563734b04acbfa36046295ff1fb45252 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Fri, 16 Feb 2024 19:42:29 -0500 Subject: [PATCH 123/221] Fix name when storing to a static --- alt/reg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alt/reg.py b/alt/reg.py index 86666a3..5568268 100755 --- a/alt/reg.py +++ b/alt/reg.py @@ -1150,7 +1150,7 @@ def handle_Store(self, ast: Store): kind, index = ast.location.kind, ast.location.idx if kind == "static": - symbol_name = f"{self.class_namespace}.static_{ast.name}" + symbol_name = f"{self.class_namespace}.static_{ast.location.name}" if imm is not None: self.asm.instr(f"@{symbol_name}") self.asm.instr(f"M={imm}") From de1c072e6a049bf42afb191650d983cf28b5b548 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Fri, 16 Feb 2024 19:43:19 -0500 Subject: [PATCH 124/221] Start on a second interpreter implemented in Jack --- alt/scheme/inspector.py | 13 ++-- alt/scheme/rvm.py | 135 ++++++++++++++++++++++++++++++++++------ 2 files changed, 124 insertions(+), 24 deletions(-) diff --git a/alt/scheme/inspector.py b/alt/scheme/inspector.py index 4b58671..256c82e 100644 --- a/alt/scheme/inspector.py +++ b/alt/scheme/inspector.py @@ -5,7 +5,9 @@ class Inspector: - def __init__(self, computer, symbols={}, rom_base=big.ROM_BASE, rom_limit=big.HEAP_BASE): + def __init__(self, computer, symbols, stack_loc, rom_base=big.ROM_BASE, rom_limit=big.HEAP_BASE): + self.stack_loc = stack_loc + def peek(addr): if rom_base <= addr < rom_limit: return computer.peek_rom(addr) @@ -105,8 +107,10 @@ def _obj(self, val): return "".join(chr(c) for c in chars) elif z == 4: # vector elems = self._obj(x) - if len(elems) != y: - raise Exception("bad vector") + if not isinstance(elems, list) or len(elems) != y: + raw = (elems, self._obj(y), z) + print(f"bad vector: {raw}") + return raw else: return elems elif z == 5: @@ -140,8 +144,7 @@ def go(addr): else: # A continuation: (stack, closure, next instr) return go(x) + [f"cont(saved={self._obj(x)}; {self._obj(y)}; {self.show_addr(z)}){self.show_addr(addr)}"] - SP = 0 - return go(self.peek(SP)) + return go(self.peek(self.stack_loc)) def show_stack(self): diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index d5b67cc..cc85a01 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -33,28 +33,47 @@ DEFAULT_TRACE_LEVEL = TRACE_FINE -def run(program, simulator, print_asm=DEFAULT_PRINT_ASM, trace_level=DEFAULT_TRACE_LEVEL, verbose_tty=True): +def run(program, interpreter, simulator, print_asm=DEFAULT_PRINT_ASM, trace_level=DEFAULT_TRACE_LEVEL, verbose_tty=True): encoded = compile(program) if print_asm: print(f"encoded program: {repr(encoded)}") - run_compiled(encoded, simulator, print_asm, trace_level, verbose_tty) + run_compiled(encoded, interpreter, simulator, print_asm, trace_level, verbose_tty) -def run_compiled(encoded, simulator, print_asm=DEFAULT_PRINT_ASM, trace_level=DEFAULT_TRACE_LEVEL, verbose_tty=True): +def run_compiled(encoded, interpreter, simulator, print_asm=DEFAULT_PRINT_ASM, trace_level=DEFAULT_TRACE_LEVEL, verbose_tty=True): - asm = AssemblySource() - - interpreter(asm) + if interpreter == "assembly": + asm = asm_interpreter() + elif interpreter == "jack": + asm = jack_interpreter() + else: + raise Exception(f"Unknown interpreter: {interpreter}") decode(encoded, asm) + # TODO: print with addresses if print_asm: for l in asm.lines: print(l) print() - instrs, symbols, statics = big.assemble(asm.lines, min_static=None, builtins=BUILTINS) + if interpreter == "assembly": + instrs, symbols, statics = big.assemble(asm.lines, min_static=None, builtins=BUILTINS) + stack_loc = BUILTINS["SP"] + pc_loc = BUILTINS["PC"] + next_rib_loc = BUILTINS["NEXT_RIB"] + interp_loop_addr = symbols.get("exec_loop") + halt_loop_addr = symbols.get("halt_loop") + elif interpreter == "jack": + from nand.solutions import solved_06 + instrs, symbols, statics = big.assemble(asm.lines, builtins=solved_06.BUILTIN_SYMBOLS) + stack_loc = statics["interpreter.static_stack"] + pc_loc = statics["interpreter.static_pc"] + next_rib_loc = statics["interpreter.static_nextRib"] + interp_loop_addr = first_loop_in_function(symbols, "Interpreter", "main") + halt_loop_addr = first_loop_in_function(symbols, "Interpreter", "halt") + print(interp_loop_addr, halt_loop_addr) assert symbols["start"] == big.ROM_BASE @@ -91,32 +110,32 @@ def show_map(label, m): def trace(computer, cycles): nonlocal last_traced_exec - inspector = Inspector(computer, symbols) + inspector = Inspector(computer, symbols, stack_loc) if (trace_level >= TRACE_COARSE - and (computer.pc == symbols["exec_loop"] or computer.pc == symbols["halt_loop"])): + and (computer.pc == interp_loop_addr or computer.pc == halt_loop_addr)): if last_traced_exec is None: print(f"{cycles:,d}:") else: print(f"{cycles:,d} (+{cycles - last_traced_exec:,d}):") last_traced_exec = cycles - print(f" stack ({inspector.show_addr(inspector.peek(0))}): {inspector.show_stack()}") + print(f" stack ({inspector.show_addr(inspector.peek(stack_loc))}): {inspector.show_stack()}") - next_rib = unsigned(inspector.peek(2)) + next_rib = unsigned(inspector.peek(next_rib_loc)) current_ribs = (next_rib - big.HEAP_BASE)//3 max_ribs = (big.HEAP_TOP - big.HEAP_BASE)//3 print(f" heap: {current_ribs:3,d} ({100*current_ribs/max_ribs:0.1f}%)") - print(f" PC: {inspector.show_addr(inspector.peek(1))}") + print(f" PC: {inspector.show_addr(inspector.peek(pc_loc))}") # # HACK? - # print(f" symbols (n..0): ({inspector.show_addr(inspector.peek(4))}) {inspector.show_stack(inspector.peek(4))}") + # print(f" symbols (n..0): ({inspector.show_addr(inspector.peek(symbol_table_loc))}) {inspector.show_stack(inspector.peek(symbol_table_loc))}") # print(f" ribs:") - # for addr in range(big.HEAP_BASE, unsigned(inspector.peek(BUILTINS["NEXT_RIB"])), 3): + # for addr in range(big.HEAP_BASE, unsigned(inspector.peek(next_rib_loc)), 3): # print(f" @{addr}; {inspector.show_obj(addr, deep=False)}") - print(f" {inspector.show_instr(inspector.peek(BUILTINS['PC']))}") - elif trace_level >= TRACE_FINE and computer.pc in symbols_by_addr and symbols_by_addr[computer.pc] != "halt_loop": + print(f" {inspector.show_instr(inspector.peek(pc_loc))}") + elif trace_level >= TRACE_FINE and computer.pc in symbols_by_addr and computer.pc != halt_loop_addr: print(f"{cycles:3,d}: ({symbols_by_addr[computer.pc]})") elif trace_level >= TRACE_ALL: print(f"{cycles:3,d}: {computer.pc}") @@ -124,7 +143,7 @@ def trace(computer, cycles): big.run(program=instrs, simulator=simulator, name="Scheme", - halt_addr=symbols["halt_loop"], + halt_addr=halt_loop_addr, trace=trace if trace_level > TRACE_NONE else None, verbose_tty=verbose_tty) @@ -457,12 +476,14 @@ def tag_rib_pointer(addr): assert BUILTINS["MAX_SLOT"] < tag_rib_pointer(FIRST_RIB) -def interpreter(asm): +def asm_interpreter(): """ROM program implementing the RVM runtime, which interprets a program stored as "ribs" in ROM and RAM. This part of the ROM is the same, independent of the Scheme program that's being interpreted. """ + asm = AssemblySource() + RIB_PROC = "rib_rib" FALSE = "rib_false" TRUE = "rib_true" @@ -680,7 +701,8 @@ def z_to_d(rib_addr_loc): def d_is_not_slot(): """Test if the value in D is a slot index, leaving 0 in D if so.""" - asm.instr("@1023") + # FIXME: choose the correct boundary when tagged ints and pointers are implemented + asm.instr("@0x03FF") # Mask for bits that can be set in a number less than 2^10 = 1024 asm.instr("A=!A") asm.instr("D=D&A") @@ -1740,6 +1762,78 @@ def return_from_primitive(): asm.label("interpreter_end") asm.blank() + return asm + + +def jack_interpreter(): + from nand.solutions import solved_10 + from alt import reg + + asm = AssemblySource() + + def init_global(comment, addr, value): + asm.comment(comment) + if isinstance(value, int) and -1 <= value <= 1: + asm.instr(f"@{addr}") + asm.instr(f"M={value}") + else: + asm.instr(f"@{value}") + asm.instr("D=A") + asm.instr(f"@{addr}") + asm.instr("M=D") + + # Because the base of the ROM isn't at address 0, we make it explicit for the assembler: + asm.label("start") + + init_global("Jack stack pointer", "SP", 256) + # Note: these two probably don't actually need to be initialized, but might contain garbage + # and confuse debugging + init_global("Jack frame pointer", "LCL", 0) + init_global("Jack arg pointer", "ARG", 0) + # THIS and THAT definitely don't need to be set up before the first function call + asm.blank() + + asm.comment("Initialize interpreter state that needs to point to data in ROM") + + init_global("Interpreter stack", "interpreter.static_stack", "rib_outer_cont") + init_global("Interpreter pc", "interpreter.static_pc", "main") + # TODO: stash pointers somewhere for the interpreter to find: + # rib_nil, _true, _false + # symbol_name_table_start/end + asm.blank() + + translator = reg.Translator(asm) + + def load_class(path): + with open(path) as f: + src_lines = f.readlines() + ast = solved_10.parse_class("".join(src_lines)) + + ir = reg.compile_class(ast) + + translator.translate_class(ir) + + for cl in "Interpreter", "Rib": + load_class(f"alt/scheme/{cl}.jack") + + asm.label("interpreter_end") + asm.blank() + + return asm + +def first_loop_in_function(symbols, class_name, function_name): + """Address of the first instruction labeled "loop_... found (probably) within the given function.""" + + function_label = f"{class_name}.{function_name}".lower() + symbols_by_addr = sorted((addr, name) for name, addr in symbols.items()) + ptr = 0 + while symbols_by_addr[ptr][1] != function_label: + ptr += 1 + ptr += 1 + while not symbols_by_addr[ptr][1].startswith("loop_"): + ptr += 1 + return symbols_by_addr[ptr][0] + def main(): import argparse @@ -1748,6 +1842,8 @@ def main(): parser.add_argument("--simulator", action="store", default="codegen", help="One of 'vector' (slower, more precise); 'codegen' (faster, default); 'compiled' (experimental)") parser.add_argument("--trace", action="store_true", help="Print each Ribbit instruction as it is interpreted. Note: runs almost 3x slower.") parser.add_argument("--print", action="store_true", help="Print interpreter assembly and compiled instructions.") + # TEMP: experimental for now + parser.add_argument("--jack", action="store_true", help="Use the Jack interpreter.") args = parser.parse_args() @@ -1757,6 +1853,7 @@ def main(): src_lines += [] + f.readlines() run("".join(src_lines), + interpreter="jack" if args.jack else "assembly", simulator=args.simulator, print_asm=args.print, trace_level=TRACE_COARSE if args.trace else TRACE_NONE) From bea72b1b644d1b9b67fc21d50bfd7c4012909fde Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Fri, 16 Feb 2024 20:04:01 -0500 Subject: [PATCH 125/221] Run tests with Jack interpreter --- alt/scheme/rvm.py | 11 ++++-- alt/scheme/test_rvm.py | 87 +++++++++++++++++++++++------------------- 2 files changed, 56 insertions(+), 42 deletions(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index cc85a01..79a6fa8 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -41,9 +41,7 @@ def run(program, interpreter, simulator, print_asm=DEFAULT_PRINT_ASM, trace_leve run_compiled(encoded, interpreter, simulator, print_asm, trace_level, verbose_tty) - -def run_compiled(encoded, interpreter, simulator, print_asm=DEFAULT_PRINT_ASM, trace_level=DEFAULT_TRACE_LEVEL, verbose_tty=True): - +def assemble(encoded, interpreter, print_asm): if interpreter == "assembly": asm = asm_interpreter() elif interpreter == "jack": @@ -103,6 +101,13 @@ def show_map(label, m): print(f"Total ROM: {total_words:5,d} ({100*total_words/rom_capacity:2.1f}%)") print() + return instrs, symbols, stack_loc, pc_loc, next_rib_loc, interp_loop_addr, halt_loop_addr + + +def run_compiled(encoded, interpreter, simulator, print_asm=DEFAULT_PRINT_ASM, trace_level=DEFAULT_TRACE_LEVEL, verbose_tty=True): + + # FIXME: Ug + instrs, symbols, stack_loc, pc_loc, next_rib_loc, interp_loop_addr, halt_loop_addr = assemble(encoded, interpreter, print_asm) last_traced_exec = None symbols_by_addr = { addr: name for (name, addr) in symbols.items() } diff --git a/alt/scheme/test_rvm.py b/alt/scheme/test_rvm.py index 9df7b07..d879f11 100755 --- a/alt/scheme/test_rvm.py +++ b/alt/scheme/test_rvm.py @@ -10,16 +10,17 @@ from nand.vector import unsigned # TODO: put this somewhere common: -def parameterize_simulators(f): +def parameterize(f): def vector(chip, **args): - return run_to_halt(chip, simulator="vector", **args) + return run_to_halt(chip, simulator="vector", interpreter="assembly", **args) def codegen(chip, **args): - return run_to_halt(chip, simulator="codegen", **args) - return pytest.mark.parametrize("run", [vector, codegen])(f) + return run_to_halt(chip, simulator="codegen", interpreter="assembly", **args) + def jack(chip, **args): + return run_to_halt(chip, simulator="codegen", interpreter="jack", **args) + return pytest.mark.parametrize("run", [vector, codegen, jack])(f) - -@parameterize_simulators -def test_trivial(run): +@parameterize +def test_trivial(run, interpreter): program = "42" inspect, output = run(program) @@ -28,7 +29,7 @@ def test_trivial(run): assert output == [] -@parameterize_simulators +@parameterize def test_string(run): program = '"abc"' @@ -38,7 +39,7 @@ def test_string(run): assert output == [] -@parameterize_simulators +@parameterize def test_lt(run): program = several("(< 1 2)", "(< 1 1)", "(< 2 1)") @@ -48,7 +49,7 @@ def test_lt(run): assert output == [] -@parameterize_simulators +@parameterize def test_add(run): program = "(+ 1 2)" @@ -58,7 +59,7 @@ def test_add(run): assert output == [] -@parameterize_simulators +@parameterize def test_sub(run): program = "(- 123 234)" @@ -68,7 +69,7 @@ def test_sub(run): assert output == [] -@parameterize_simulators +@parameterize def test_mul(run): program = "(* 6 7)" @@ -78,7 +79,7 @@ def test_mul(run): assert output == [] -@parameterize_simulators +@parameterize def test_mul_mixed_signs(run): program = several("(* 6 -7)", "(* -14 -3)", "(* 2 -21)") @@ -88,7 +89,7 @@ def test_mul_mixed_signs(run): assert output == [] -@parameterize_simulators +@parameterize def test_if(run): program = "(if #t 42 '())" @@ -98,7 +99,7 @@ def test_if(run): assert output == [] -@parameterize_simulators +@parameterize def test_quote(run): program = "'()" @@ -108,7 +109,7 @@ def test_quote(run): assert output == [] -@parameterize_simulators +@parameterize def test_lambda(run): program = """ ((lambda (x y) (+ x y)) @@ -121,7 +122,7 @@ def test_lambda(run): assert output == [] -@parameterize_simulators +@parameterize def test_define(run): program = """ (define (cons x y) (rib x y 0)) @@ -135,7 +136,7 @@ def test_define(run): assert output == [] -@parameterize_simulators +@parameterize def test_fact(run): program = """ (define (fact n) @@ -151,7 +152,7 @@ def test_fact(run): assert output == [] -@parameterize_simulators +@parameterize def test_capture(run): program = """ (define (add x) (lambda (y) (+ x y))) @@ -164,7 +165,7 @@ def test_capture(run): assert output == [] -@parameterize_simulators +@parameterize def test_draw(run): program = """ (define poke (rib 21 0 1)) @@ -178,7 +179,7 @@ def test_draw(run): assert output == [] -@parameterize_simulators +@parameterize def test_tty(run): program = """ (define poke (rib 21 0 1)) @@ -197,7 +198,7 @@ def test_tty(run): # Tests for specific primitives: # -@parameterize_simulators +@parameterize def test_field0(run): program = """ (define field0 (rib 6 0 1)) @@ -211,7 +212,7 @@ def test_field0(run): assert output == [] -@parameterize_simulators +@parameterize def test_field1(run): program = """ (define field1 (rib 7 0 1)) @@ -224,7 +225,7 @@ def test_field1(run): assert inspect.stack() == [[8, 9]] # cdr assert output == [] -@parameterize_simulators +@parameterize def test_field2(run): program = """ (define field2 (rib 8 0 1)) @@ -238,7 +239,7 @@ def test_field2(run): assert output == [] -@parameterize_simulators +@parameterize def test_field0_set(run): program = """ (define field0-set! (rib 9 0 1)) @@ -256,7 +257,7 @@ def test_field0_set(run): assert output == [] -@parameterize_simulators +@parameterize def test_field1_set(run): program = """ (define field1-set! (rib 10 0 1)) @@ -275,7 +276,7 @@ def test_field1_set(run): assert output == [] -@parameterize_simulators +@parameterize def test_field2_set(run): program = """ (define field2-set! (rib 11 0 1)) @@ -293,7 +294,7 @@ def test_field2_set(run): assert output == [] -@parameterize_simulators +@parameterize def test_eqv_simple(run): program = """ (define eqv? (rib 12 0 1)) @@ -310,7 +311,7 @@ def test_eqv_simple(run): assert output == [] -@parameterize_simulators +@parameterize def test_eqv_ribs(run): program = """ (define eqv? (rib 12 0 1)) @@ -327,7 +328,7 @@ def test_eqv_ribs(run): assert output == [] -@parameterize_simulators +@parameterize def test_ribq(run): program = """ (define rib? (rib 5 0 1)) @@ -355,7 +356,7 @@ def go(xs): return "(define (cons $$x $$y) (rib $$x $$y 0))\n" + go(list(exprs)) -def run_to_halt(program, max_cycles=20000, simulator="codegen"): +def run_to_halt(program, interpreter, max_cycles=20000, simulator="codegen"): """Compile and run a Scheme program, then return a function for inspecting the RAM, and a list of words that were written to the TTY port. """ @@ -363,13 +364,21 @@ def run_to_halt(program, max_cycles=20000, simulator="codegen"): encoded = rvm.compile(program) # print(f"encoded program: {repr(encoded)}") - asm = AssemblySource() + instrs, symbols, stack_loc, pc_loc, next_rib_loc, interp_loop_addr, halt_loop_addr = rvm.assemble(encoded, interpreter, False) + + # asm = AssemblySource() + + # if interpreter == "assembly": + # asm = rvm.asm_interpreter() + # elif interpreter == "jack": + # asm = rvm.jack_interpreter() - rvm.interpreter(asm) + # rvm.decode(encoded, asm) - rvm.decode(encoded, asm) + # instrs, symbols, _ = big.assemble(asm.lines, min_static=None, builtins=rvm.BUILTINS) - instrs, symbols, _ = big.assemble(asm.lines, min_static=None, builtins=rvm.BUILTINS) + # for k, v in symbols.items(): + # print(k, v) computer = nand.syntax.run(big.BigComputer, simulator=simulator) @@ -382,9 +391,9 @@ def run_to_halt(program, max_cycles=20000, simulator="codegen"): cycles = 0 output = [] - inspect = Inspector(computer, symbols) + inspect = Inspector(computer, symbols, stack_loc) - while (not computer.fetch or computer.pc != symbols["halt_loop"]) and cycles <= max_cycles: + while (not computer.fetch or computer.pc != halt_loop_addr) and cycles <= max_cycles: computer.ticktock() cycles += 1 @@ -396,10 +405,10 @@ def run_to_halt(program, max_cycles=20000, simulator="codegen"): if computer.fetch and inspect.is_labeled(computer.pc): cpu_pc = inspect.show_addr(computer.pc) print(f"{cpu_pc}") - if cpu_pc == "@exec_loop": + if computer.pc == interp_loop_addr: stack = ", ".join(str(x) for x in inspect.stack()) print(f" stack: {stack}") - pc = inspect.peek(1) + pc = inspect.peek(pc_loc) print(f" {inspect.show_addr(pc):<10} {inspect.show_instr(pc)}") return (inspect, output) From ead7ec099a874987d797369d66f265d80ff59ef9 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Fri, 16 Feb 2024 20:05:00 -0500 Subject: [PATCH 126/221] Jack interpreter: only const implemented --- alt/scheme/Interpreter.jack | 107 ++++++++++++++++++++++++++++++++++++ alt/scheme/Rib.jack | 18 ++++++ 2 files changed, 125 insertions(+) create mode 100644 alt/scheme/Interpreter.jack create mode 100644 alt/scheme/Rib.jack diff --git a/alt/scheme/Interpreter.jack b/alt/scheme/Interpreter.jack new file mode 100644 index 0000000..71607e9 --- /dev/null +++ b/alt/scheme/Interpreter.jack @@ -0,0 +1,107 @@ +class Interpreter { + // For any kind of efficiency, the compiler wants to allocate these pointers as statics in the + // usual range (starting at addr 16). And ordinarily its own stack state is stored in + // locations 0-3 or so. + // For that matter, its stack grows up from 256 (which is fine as long as it doesn't collide + // with the screen buffer at 2048.) + static Rib stack; + static Rib pc; + static Rib nextRib; + + // Pre-allocated "proc" containing the "rib" primitive, which will be used to construct every + // other primitive. + static Rib ribRib; + + // Pre-allocated special values: + static Rib ribFalse; + static Rib ribTrue; + static Rib ribNil; + + + /* + When we arrive here, some initialization has already occurred. + TODO: how does that actually work? + */ + function void main() { + var Array instr; + var int opcode; + + // Initialize some more state: + + // Bottom of the "heap" area: + let nextRib = 32767 + 1; // Note: will overflow to -32768 + + // TODO: initialize the symbol table + + // Skip the bogus "main" instr: + let pc = pc[2]; + + while (1) { + let opcode = pc[0]; + + if (opcode = 0) { + if (instr[2] = 0) { + // jump + do Interpreter.halt(); // TODO + } + else { + // call + do Interpreter.halt(); // TODO + } + } + else { + if (opcode = 1) { + // set + do Interpreter.halt(); // TODO + } + else { + if (opcode = 2) { + // get + do Interpreter.halt(); // TODO + } + else { + if (opcode = 3) { + // const + do Interpreter.push(pc[1]); + let pc = pc[2]; + } + else { + if (opcode = 4) { + // if + do Interpreter.halt(); // TODO + } + else { + // if (opcode = 5) { + // halt + do Interpreter.halt(); + // } + }}}}} + } + + return; + } + + /** Allocate a rib on the heap, filling in the three fields. */ + function Rib alloc(int x, int y, int z) { + var Rib r; + let r = nextRib; + let r[0] = x; + let r[1] = y; + let r[2] = z; + let nextRib = nextRib + 3; + return r; + } + + function void push(int obj) { + var Rib entry; + + let stack = Interpreter.alloc(obj, stack, 0); // pair-type + + return; + } + + function void halt() { + while (1) { + } + } +} diff --git a/alt/scheme/Rib.jack b/alt/scheme/Rib.jack new file mode 100644 index 0000000..7d31958 --- /dev/null +++ b/alt/scheme/Rib.jack @@ -0,0 +1,18 @@ +class Rib { + field int x, y, z; + + /** + TEMP: for now every rib pointer is definitely above ROM_BASE (4096), so any value smaller + than that is considered an un-boxed int. We'll also treat negative values in teh same range + as raw ints. + This is all bogus. Need to implement a proper tagging scheme so we can use larger int values + (i.e. 15 bits.) + */ + function boolean isRib(int obj) { + return (obj < -4095) | (obj > 4095); + } + + method int x() { + return x; + } +} From d03fb32e80d1963181603e015db6ccaba95a76b1 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sat, 17 Feb 2024 17:34:42 -0500 Subject: [PATCH 127/221] Add the initial value for each symbol to the table in ROM --- alt/scheme/rvm.py | 49 ++++++++++++++++------------------------------- 1 file changed, 16 insertions(+), 33 deletions(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 79a6fa8..4138be8 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -292,13 +292,16 @@ def emit_vector(lbl, elems, count: int): emit_rib(lbl, elems, f"#{count}", "#4" # TODO: move this table elsewhere, so the ribs for strings and instructions form a monolithic # block of address space? - # TODO: add the initial value for each symbol to this table - asm.comment("Table of pointers to symbol name ribs in ROM:") + asm.comment("Table of pointers to symbol name and initial value ribs in ROM:") asm.label("symbol_names_start") - for lbl, s in reversed(sym_names): - if s != "": - asm.comment(f'"{s}"') + sym_names_and_values = list(zip( + sym_names, + ["rib_rib", "rib_false", "rib_true", "rib_nil"] + ["rib_false"]*(len(sym_names)-4))) + for i in reversed(range(len(sym_names_and_values))): + (lbl, s), val = sym_names_and_values[i] + asm.comment(f'{i}: "{s}"') asm.instr(f"@{lbl}") + asm.instr(f"@{val}") asm.label("symbol_names_end") asm.blank() @@ -577,9 +580,10 @@ def push(val="D"): # asm.instr("@KEYBOARD") # asm.instr("M=D") - asm.comment("new symbol, with value = false") - asm.instr("@rib_false") - asm.instr("D=A") + asm.comment("new symbol, with value = MEM(R5+1) and name = MEM(R5)") + asm.instr("@TEMP_0") + asm.instr("A=M+1") + asm.instr("D=M") rib_append() asm.instr("@TEMP_0") asm.instr("A=M") @@ -621,9 +625,11 @@ def push(val="D"): # asm.instr("@KEYBOARD") # asm.instr("M=D") - asm.comment("increment R5") + asm.comment("increment R5 (by 2)") + asm.instr("@2") + asm.instr("D=A") asm.instr("@TEMP_0") - asm.instr("M=M+1") + asm.instr("M=M+D") asm.comment("D = compare(R5, symbol_names_end)") asm.instr("D=M") @@ -634,29 +640,6 @@ def push(val="D"): asm.blank() - # TODO: fold this into the table of symbol names; cleaner and smaller - asm.comment("HACK: initialize standard symbols (rib, false, true, nil)") - def inject_symbol_value(idx, val): - asm.comment(f"global({idx}) = {val}") - asm.instr("@NEXT_RIB") - asm.instr("D=M") - asm.instr(f"@{(idx+1)*6}") - asm.instr("D=D-A") - asm.instr("@TEMP_0") - asm.instr("M=D") - # asm.instr("@KEYBOARD"); asm.instr("M=D") # HACK - asm.instr(val) - asm.instr("D=A") - asm.instr("@TEMP_0") - asm.instr("A=M") - asm.instr("M=D") - inject_symbol_value(0, "@rib_rib") - inject_symbol_value(1, "@rib_false") - inject_symbol_value(2, "@rib_true") - inject_symbol_value(3, "@rib_nil") - - asm.blank() - # # Initialize interpreter state: From 6dbc87d7add643ccdde12c8eefff8d5a3779d678 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sat, 17 Feb 2024 17:41:03 -0500 Subject: [PATCH 128/221] Initialize the symbol table --- alt/scheme/Interpreter.jack | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/alt/scheme/Interpreter.jack b/alt/scheme/Interpreter.jack index 71607e9..8dc6eab 100644 --- a/alt/scheme/Interpreter.jack +++ b/alt/scheme/Interpreter.jack @@ -17,6 +17,11 @@ class Interpreter { static Rib ribTrue; static Rib ribNil; + // Used only during intialization: + static Array symbolNameTableStart; + static Array symbolNameTableEnd; + + static Array tty; // DEBUG /* When we arrive here, some initialization has already occurred. @@ -28,10 +33,12 @@ class Interpreter { // Initialize some more state: + let tty = 4095; // DEBUG + // Bottom of the "heap" area: let nextRib = 32767 + 1; // Note: will overflow to -32768 - // TODO: initialize the symbol table + do Interpreter.initSymbolTable(); // Skip the bogus "main" instr: let pc = pc[2]; @@ -81,10 +88,34 @@ class Interpreter { return; } + /** + Allocate symbols (which must be in RAM so their values can be updated in place) and the + pairs that form the list that is the symbol table. + The decoded instructions in ROM refer to the addresses where these symbols are expected to + located in memory. + */ + function void initSymbolTable() { + var Array ptr; + var Rib symbol; + var Rib entry; + + let entry = ribNil; + let ptr = symbolNameTableStart; + while (ptr < symbolNameTableEnd) { + let tty[0] = ptr; // DEBUG + let symbol = Interpreter.alloc(ptr[1], ptr[0], 2); // symbol type + let entry = Interpreter.alloc(symbol, entry, 0); // pair type + let ptr = ptr + 2; + } + + return; + } + /** Allocate a rib on the heap, filling in the three fields. */ function Rib alloc(int x, int y, int z) { var Rib r; let r = nextRib; + let tty[0] = r; // DEBUG let r[0] = x; let r[1] = y; let r[2] = z; From 11dd42b39a2b6d0abf9c0f51cc7476aab4420682 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sat, 17 Feb 2024 19:43:24 -0500 Subject: [PATCH 129/221] First passing test for the Jack interpreter: test_add --- alt/scheme/Interpreter.jack | 261 ++++++++++++++++++++++++++++++++++-- alt/scheme/rvm.py | 13 +- 2 files changed, 259 insertions(+), 15 deletions(-) diff --git a/alt/scheme/Interpreter.jack b/alt/scheme/Interpreter.jack index 8dc6eab..5b9034a 100644 --- a/alt/scheme/Interpreter.jack +++ b/alt/scheme/Interpreter.jack @@ -24,12 +24,14 @@ class Interpreter { static Array tty; // DEBUG /* - When we arrive here, some initialization has already occurred. - TODO: how does that actually work? + When we arrive here, the assembly prelude has already initialized many of the statics with the + starting values. */ function void main() { - var Array instr; var int opcode; + var Rib symbol; + var Rib proc; + var Rib cont; // Initialize some more state: @@ -47,19 +49,74 @@ class Interpreter { let opcode = pc[0]; if (opcode = 0) { - if (instr[2] = 0) { + if (pc[2] = 0) { // jump - do Interpreter.halt(); // TODO + if ((pc[1] > -1) & (pc[1] < 1000)) { + // y is slot # of target + // TODO: let proc = findSlot(); + do Interpreter.halt(); // TODO + } + else { + // y is addr of target symbol + let symbol = pc[1]; + let proc = symbol[0]; + if (proc[0] & (~31)) { // not between 0 and 31 + // closure + + do Interpreter.halt(); // TODO + } + else { + // primitive + + do Interpreter.handlePrimitive(proc[0]); + + let cont = Interpreter.findContinuation(); + + // Overwrite top stack entry so the stack consists of the just-pushed + // result on top of the saved stack from the continuation. + let stack[1] = cont[0]; + + // PC = next instruction from the continuation + let pc = cont[2]; + } + } } else { // call - do Interpreter.halt(); // TODO + if ((pc[1] > -1) & (pc[1] < 1000)) { + // y is slot # of target + // TODO: let proc = findSlot(); + do Interpreter.halt(); // TODO + } + else { + // y is addr of target symbol + let symbol = pc[1]; + let proc = symbol[0]; + if (proc[0] & (~31)) { // not between 0 and 31 + // closure + do Interpreter.halt(); // TODO + } + else { + // primitive + do Interpreter.handlePrimitive(proc[0]); + } + } + let pc = pc[2]; } } else { if (opcode = 1) { // set - do Interpreter.halt(); // TODO + if ((pc[1] > -1) & (pc[1] < 1000)) { + // y is slot # of target + do Interpreter.halt(); // TODO + } + else { + // y is addr of target symbol + let symbol = pc[1]; + let symbol[0] = Interpreter.pop(); + } + let pc = pc[2]; } else { if (opcode = 2) { @@ -102,7 +159,7 @@ class Interpreter { let entry = ribNil; let ptr = symbolNameTableStart; while (ptr < symbolNameTableEnd) { - let tty[0] = ptr; // DEBUG + // let tty[0] = ptr; // DEBUG let symbol = Interpreter.alloc(ptr[1], ptr[0], 2); // symbol type let entry = Interpreter.alloc(symbol, entry, 0); // pair type let ptr = ptr + 2; @@ -111,11 +168,171 @@ class Interpreter { return; } + function void handlePrimitive(int opcode) { + var int x, y, z; + var Rib tmp; + + // Note: What you really want here is a computed jump (i.e. a switch construct.) + // Failing that, we make do with a binary search, effectively, with 5 branches + // to examine the 5 bits of the opcode. + + if (opcode < 16) { + if (opcode < 8) { + if (opcode < 4) { + if (opcode < 2) { + if (opcode < 1) { + // 0 + // rib: x y z -- rib(x, y, z) + // Note: one rib becomes garbage (top entry of stack) + + let z = stack[0]; + + let tmp = stack[1]; + let y = tmp[0]; + + // The entry holding x will be the new top of stack: + let stack = tmp[1]; + let x = stack[0]; + + // Now re-use the second entry's rib as the newly-constructed rib: + let tmp[0] = x; + let tmp[1] = y; + let tmp[2] = z; + let stack[0] = tmp; + } + else { + // 1 + // TODO + let tty[0] = opcode; // DEBUG + do Intepreter.halt(); + } + } + else { // opcode >= 2 + if (opcode < 3) { + // 2 + // TODO + let tty[0] = opcode; // DEBUG + do Intepreter.halt(); + } + else { + // 3 + // TODO + let tty[0] = opcode; // DEBUG + do Intepreter.halt(); + } + } + } + else { // opcode >= 4 + if (opcode < 6) { + if (opcode < 5) { + // 4 + // TODO + let tty[0] = opcode; // DEBUG + do Intepreter.halt(); + } + else { + // 5 + // TODO + let tty[0] = opcode; // DEBUG + do Intepreter.halt(); + } + } + else { // opcode >= 6 + if (opcode < 7) { + // 6 + // TODO + let tty[0] = opcode; // DEBUG + do Intepreter.halt(); + } + else { + // 7 + // TODO + let tty[0] = opcode; // DEBUG + do Intepreter.halt(); + } + } + } + } + else { // opcode >= 8 + if (opcode < 12) { + if (opcode < 10) { + if (opcode < 9) { + // 8 + // TODO + let tty[0] = opcode; // DEBUG + do Intepreter.halt(); + } + else { + // 9 + // TODO + let tty[0] = opcode; // DEBUG + do Intepreter.halt(); + } + } + else { // opcode >= 10 + if (opcode < 11) { + // 10 + // TODO + let tty[0] = opcode; // DEBUG + do Intepreter.halt(); + } + else { + // 11 + // TODO + let tty[0] = opcode; // DEBUG + do Intepreter.halt(); + } + } + } + else { // opcode >= 12 + if (opcode < 14) { + if (opcode < 13) { + // 12 + // TODO + let tty[0] = opcode; // DEBUG + do Intepreter.halt(); + } + else { + // 13 + // TODO + let tty[0] = opcode; // DEBUG + do Intepreter.halt(); + } + } + else { // opcode >= 14 + if (opcode < 15) { + // 14 + // +: x y -- (x + y) + + // Note: one rib becomes garbage: + let y = Interpreter.pop(); + let x = stack[0]; + // Update second stack entry in place: + let stack[0] = x + y; + } + else { + // 15 + // TODO + let tty[0] = opcode; // DEBUG + do Intepreter.halt(); + } + } + } + } + } + else { + let tty[0] = opcode; // DEBUG + do Intepreter.halt(); + } + return; + } + + /** Allocate a rib on the heap, filling in the three fields. */ function Rib alloc(int x, int y, int z) { var Rib r; let r = nextRib; - let tty[0] = r; // DEBUG + // let tty[0] = r; // DEBUG let r[0] = x; let r[1] = y; let r[2] = z; @@ -124,15 +341,35 @@ class Interpreter { } function void push(int obj) { - var Rib entry; - let stack = Interpreter.alloc(obj, stack, 0); // pair-type - return; } + /** Discard the top entry from the stack, return its CAR. */ + function int pop() { + var int r; + let r = stack[0]; + let stack = stack[1]; + return r; + } + + /** + Address of the continuation rib in the current stack frame: the first entry in the + stack with z != 0. + */ + function Rib findContinuation() { + var Rib ptr; + + let ptr = stack; + while (ptr[2] = 0) { + let ptr = ptr[1]; + } + return ptr; + } + function void halt() { while (1) { + // let tty[0] = -1; // DEBUG } } } diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 4138be8..053f2b1 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -1785,9 +1785,16 @@ def init_global(comment, addr, value): init_global("Interpreter stack", "interpreter.static_stack", "rib_outer_cont") init_global("Interpreter pc", "interpreter.static_pc", "main") - # TODO: stash pointers somewhere for the interpreter to find: - # rib_nil, _true, _false - # symbol_name_table_start/end + + init_global("Primitive proc: 'rib'", "interpreter.static_ribRib", "rib_rib") + + init_global("Constant: #f", "interpreter.static_ribFalse", "rib_false") + init_global("Constant: #t", "interpreter.static_ribTrue", "rib_true") + init_global("Constant: '()", "interpreter.static_ribNil", "rib_nil") + + init_global("Symbol Names (start)", "interpreter.static_symbolNameTableStart", "symbol_names_start") + init_global("Symbol Names (end)", "interpreter.static_symbolNameTableEnd", "symbol_names_end") + asm.blank() translator = reg.Translator(asm) From 836bfa4c9b979456dc1fd988450832cc8ec1830e Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sat, 17 Feb 2024 19:47:40 -0500 Subject: [PATCH 130/221] =?UTF-8?q?Implement=20primitive=20=E2=80=9Cid?= =?UTF-8?q?=E2=80=9D=20for=20Jack?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- alt/scheme/Interpreter.jack | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/alt/scheme/Interpreter.jack b/alt/scheme/Interpreter.jack index 5b9034a..7a3a506 100644 --- a/alt/scheme/Interpreter.jack +++ b/alt/scheme/Interpreter.jack @@ -202,9 +202,9 @@ class Interpreter { } else { // 1 - // TODO - let tty[0] = opcode; // DEBUG - do Intepreter.halt(); + // id: -- + + // Literally nothing to see here } } else { // opcode >= 2 From 856d37fc11cfa6edfb07561d0aec97b9c99688d7 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sat, 17 Feb 2024 19:50:59 -0500 Subject: [PATCH 131/221] Implement get (symbol) in Jack --- alt/scheme/Interpreter.jack | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/alt/scheme/Interpreter.jack b/alt/scheme/Interpreter.jack index 7a3a506..1201863 100644 --- a/alt/scheme/Interpreter.jack +++ b/alt/scheme/Interpreter.jack @@ -121,7 +121,16 @@ class Interpreter { else { if (opcode = 2) { // get - do Interpreter.halt(); // TODO + if ((pc[1] > -1) & (pc[1] < 1000)) { + // y is slot # of target + do Interpreter.halt(); // TODO + } + else { + // y is addr of target symbol + let symbol = pc[1]; + do Interpreter.push(symbol[0]); + } + let pc = pc[2]; } else { if (opcode = 3) { From c427c00b5359e06e3b8d045f9dd873449e3b1c98 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sat, 17 Feb 2024 19:56:57 -0500 Subject: [PATCH 132/221] Unbreak trivial test for nw parameterization --- alt/scheme/test_rvm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alt/scheme/test_rvm.py b/alt/scheme/test_rvm.py index d879f11..ca03a2b 100755 --- a/alt/scheme/test_rvm.py +++ b/alt/scheme/test_rvm.py @@ -20,7 +20,7 @@ def jack(chip, **args): return pytest.mark.parametrize("run", [vector, codegen, jack])(f) @parameterize -def test_trivial(run, interpreter): +def test_trivial(run): program = "42" inspect, output = run(program) From 875df7471173acd9fa6ab30e21cadb7172e2c159 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sat, 17 Feb 2024 19:57:15 -0500 Subject: [PATCH 133/221] A slightly more interesting test for quotation --- alt/scheme/test_rvm.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/alt/scheme/test_rvm.py b/alt/scheme/test_rvm.py index ca03a2b..28ee812 100755 --- a/alt/scheme/test_rvm.py +++ b/alt/scheme/test_rvm.py @@ -101,11 +101,11 @@ def test_if(run): @parameterize def test_quote(run): - program = "'()" + program = "'(1 2 3)" inspect, output = run(program) - assert inspect.stack() == [[]] + assert inspect.stack() == [[1, 2, 3]] assert output == [] From 44dfb6acd0d0ff1b0fc1d0b54ce0748501d2b07b Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sat, 17 Feb 2024 21:13:17 -0500 Subject: [PATCH 134/221] Implement call (closure) and get (slot) in Jack --- alt/scheme/Interpreter.jack | 54 ++++++++++++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 6 deletions(-) diff --git a/alt/scheme/Interpreter.jack b/alt/scheme/Interpreter.jack index 1201863..ca0afaa 100644 --- a/alt/scheme/Interpreter.jack +++ b/alt/scheme/Interpreter.jack @@ -30,8 +30,9 @@ class Interpreter { function void main() { var int opcode; var Rib symbol; - var Rib proc; - var Rib cont; + var Rib proc, code, cont, newStack; + var int slot; + var Rib ptr; // Initialize some more state: @@ -94,14 +95,27 @@ class Interpreter { let proc = symbol[0]; if (proc[0] & (~31)) { // not between 0 and 31 // closure - do Interpreter.halt(); // TODO + + // New continuation: + // x = saved stack (after popping args) + // y = proc rib + // z = next instruction + let cont = Interpreter.alloc(-1, proc, pc[2]); + let newStack = Interpreter.wrangleClosureParams(proc, cont); + let cont[0] = stack; + let stack = newStack; + + // Now jump to the entry point of the proc: + let code = proc[0]; + let pc = code[2]; } else { // primitive do Interpreter.handlePrimitive(proc[0]); + + let pc = pc[2]; } } - let pc = pc[2]; } } else { @@ -121,9 +135,15 @@ class Interpreter { else { if (opcode = 2) { // get - if ((pc[1] > -1) & (pc[1] < 1000)) { + let slot = pc[1]; + if ((slot > -1) & (slot < 1000)) { // y is slot # of target - do Interpreter.halt(); // TODO + let ptr = stack; + while (slot > 0) { + let ptr = ptr[1]; + let slot = slot - 1; + } + do Interpreter.push(ptr[0]); } else { // y is addr of target symbol @@ -154,6 +174,8 @@ class Interpreter { return; } + + /** Allocate symbols (which must be in RAM so their values can be updated in place) and the pairs that form the list that is the symbol table. @@ -177,6 +199,26 @@ class Interpreter { return; } + /** + Pop numArgs objects from the stack, assembling them into a new stack (in reverse order), + on top of the just-allocated continuation rib. + */ + function Rib wrangleClosureParams(Rib proc, Rib cont) { + var Rib code; + var int numArgs, i; + var Rib newStack; + + let code = proc[0]; + let numArgs = code[0]; + let i = 0; + let newStack = cont; + while (i < numArgs) { + let newStack = Interpreter.alloc(Interpreter.pop(), newStack, 0); + let i = i + 1; + } + return newStack; + } + function void handlePrimitive(int opcode) { var int x, y, z; var Rib tmp; From 7608341b6d13547b40ec617ff2c49885c27c72f3 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sat, 17 Feb 2024 21:40:19 -0500 Subject: [PATCH 135/221] Implement jump (closure); factor out handling of slot/global target --- alt/scheme/Interpreter.jack | 157 +++++++++++++++++++----------------- 1 file changed, 83 insertions(+), 74 deletions(-) diff --git a/alt/scheme/Interpreter.jack b/alt/scheme/Interpreter.jack index ca0afaa..1ec529a 100644 --- a/alt/scheme/Interpreter.jack +++ b/alt/scheme/Interpreter.jack @@ -52,104 +52,86 @@ class Interpreter { if (opcode = 0) { if (pc[2] = 0) { // jump - if ((pc[1] > -1) & (pc[1] < 1000)) { - // y is slot # of target - // TODO: let proc = findSlot(); - do Interpreter.halt(); // TODO + + let symbol = Interpreter.getTarget(pc[1]); // actually an entry or symbol + let proc = symbol[0]; + + if (proc[0] & (~31)) { // not between 0 and 31 + // closure + + let cont = Interpreter.findContinuation(); + + // Note: can't overwrite the old continuation because it may be in ROM + let cont = Interpreter.alloc(cont[0], proc, cont[2]); + + let stack = Interpreter.wrangleClosureParams(proc, cont); + + let code = proc[0]; + let pc = code[2]; } else { - // y is addr of target symbol - let symbol = pc[1]; - let proc = symbol[0]; - if (proc[0] & (~31)) { // not between 0 and 31 - // closure + // primitive - do Interpreter.halt(); // TODO - } - else { - // primitive - - do Interpreter.handlePrimitive(proc[0]); + do Interpreter.handlePrimitive(proc[0]); - let cont = Interpreter.findContinuation(); + let cont = Interpreter.findContinuation(); - // Overwrite top stack entry so the stack consists of the just-pushed - // result on top of the saved stack from the continuation. - let stack[1] = cont[0]; + // Overwrite top stack entry so the stack consists of the just-pushed + // result on top of the saved stack from the continuation. + let stack[1] = cont[0]; - // PC = next instruction from the continuation - let pc = cont[2]; - } + // PC = next instruction from the continuation + let pc = cont[2]; } } else { // call - if ((pc[1] > -1) & (pc[1] < 1000)) { - // y is slot # of target - // TODO: let proc = findSlot(); - do Interpreter.halt(); // TODO + + let symbol = Interpreter.getTarget(pc[1]); // actually an entry or symbol + let proc = symbol[0]; + + if (proc[0] & (~31)) { // not between 0 and 31 + // closure + + // New continuation: + // x = saved stack (after popping args) + // y = proc rib + // z = next instruction + let cont = Interpreter.alloc(-1, proc, pc[2]); + let newStack = Interpreter.wrangleClosureParams(proc, cont); + let cont[0] = stack; + let stack = newStack; + + // Now jump to the entry point of the proc: + let code = proc[0]; + let pc = code[2]; } else { - // y is addr of target symbol - let symbol = pc[1]; - let proc = symbol[0]; - if (proc[0] & (~31)) { // not between 0 and 31 - // closure - - // New continuation: - // x = saved stack (after popping args) - // y = proc rib - // z = next instruction - let cont = Interpreter.alloc(-1, proc, pc[2]); - let newStack = Interpreter.wrangleClosureParams(proc, cont); - let cont[0] = stack; - let stack = newStack; - - // Now jump to the entry point of the proc: - let code = proc[0]; - let pc = code[2]; - } - else { - // primitive - do Interpreter.handlePrimitive(proc[0]); + // primitive + do Interpreter.handlePrimitive(proc[0]); - let pc = pc[2]; - } + let pc = pc[2]; } } } else { if (opcode = 1) { // set - if ((pc[1] > -1) & (pc[1] < 1000)) { - // y is slot # of target - do Interpreter.halt(); // TODO - } - else { - // y is addr of target symbol - let symbol = pc[1]; - let symbol[0] = Interpreter.pop(); - } + + let symbol = Interpreter.getTarget(pc[1]); // actually an entry or symbol + + let symbol[0] = Interpreter.pop(); + let pc = pc[2]; } else { if (opcode = 2) { // get - let slot = pc[1]; - if ((slot > -1) & (slot < 1000)) { - // y is slot # of target - let ptr = stack; - while (slot > 0) { - let ptr = ptr[1]; - let slot = slot - 1; - } - do Interpreter.push(ptr[0]); - } - else { - // y is addr of target symbol - let symbol = pc[1]; - do Interpreter.push(symbol[0]); - } + + let symbol = Interpreter.getTarget(pc[1]); // actually an entry or symbol + + do Interpreter.push(symbol[0]); + let pc = pc[2]; } else { @@ -199,6 +181,33 @@ class Interpreter { return; } + /** + Decode the "y" value from a jump/.call, set, or get instruction, and return the rib that + contains the target, which might be a stack entry or a symbol. In either case, the actual + target is found in the "x" field of the result. + */ + function Rib getTarget(int slotOrGlobal) { + var int i; + var Rib ptr; + var Rib symbol; + + if ((slotOrGlobal > -1) & (slotOrGlobal < 1000)) { + let i = slotOrGlobal; + // y is slot # of target + let ptr = stack; + while (i > 0) { + let ptr = ptr[1]; + let i = i - 1; + } + return ptr; + } + else { + // y is addr of target symbol + let symbol = pc[1]; + return symbol; + } + } + /** Pop numArgs objects from the stack, assembling them into a new stack (in reverse order), on top of the just-allocated continuation rib. From 7a8e698bff347c9345b4d51459a180a0224cc3b8 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sat, 17 Feb 2024 21:41:14 -0500 Subject: [PATCH 136/221] Implement < primitive --- alt/scheme/Interpreter.jack | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/alt/scheme/Interpreter.jack b/alt/scheme/Interpreter.jack index 1ec529a..7482645 100644 --- a/alt/scheme/Interpreter.jack +++ b/alt/scheme/Interpreter.jack @@ -354,9 +354,18 @@ class Interpreter { } else { // 13 - // TODO - let tty[0] = opcode; // DEBUG - do Intepreter.halt(); + // <: x y -- bool(x < y) + + // Note: one rib becomes garbage: + let y = Interpreter.pop(); + let x = stack[0]; + // Update second stack entry in place: + if (x < y) { + let stack[0] = ribTrue; + } + else { + let stack[0] = ribFalse; + } } } else { // opcode >= 14 From 2ef6772fc9bf082502c5a35475b7bb3f9fb9de34 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sat, 17 Feb 2024 21:44:10 -0500 Subject: [PATCH 137/221] Implement - primitive --- alt/scheme/Interpreter.jack | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/alt/scheme/Interpreter.jack b/alt/scheme/Interpreter.jack index 7482645..d330dca 100644 --- a/alt/scheme/Interpreter.jack +++ b/alt/scheme/Interpreter.jack @@ -381,9 +381,13 @@ class Interpreter { } else { // 15 - // TODO - let tty[0] = opcode; // DEBUG - do Intepreter.halt(); + // -: x y -- (x - y) + + // Note: one rib becomes garbage: + let y = Interpreter.pop(); + let x = stack[0]; + // Update second stack entry in place: + let stack[0] = x - y; } } } From 363ce2ad73e66e734870eb15a433b4d8fd42557d Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sat, 17 Feb 2024 21:48:10 -0500 Subject: [PATCH 138/221] =?UTF-8?q?Implement=20=E2=80=9Cif=E2=80=9D=20inst?= =?UTF-8?q?ruction?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- alt/scheme/Interpreter.jack | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/alt/scheme/Interpreter.jack b/alt/scheme/Interpreter.jack index d330dca..ff7a193 100644 --- a/alt/scheme/Interpreter.jack +++ b/alt/scheme/Interpreter.jack @@ -33,6 +33,7 @@ class Interpreter { var Rib proc, code, cont, newStack; var int slot; var Rib ptr; + var int obj; // Initialize some more state: @@ -108,6 +109,7 @@ class Interpreter { } else { // primitive + do Interpreter.handlePrimitive(proc[0]); let pc = pc[2]; @@ -137,13 +139,22 @@ class Interpreter { else { if (opcode = 3) { // const + do Interpreter.push(pc[1]); + let pc = pc[2]; } else { if (opcode = 4) { // if - do Interpreter.halt(); // TODO + + let obj = Interpreter.pop(); + if (obj = ribFalse) { + let pc = pc[2]; + } + else { + let pc = pc[1]; + } } else { // if (opcode = 5) { From ab8156b4bbee4bef125d91eb0f9e0567be83a667 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sat, 17 Feb 2024 21:57:16 -0500 Subject: [PATCH 139/221] =?UTF-8?q?Implement=20=E2=80=9Cclose=E2=80=9D=20p?= =?UTF-8?q?rimitive=20(in=20Jack)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- alt/scheme/Interpreter.jack | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/alt/scheme/Interpreter.jack b/alt/scheme/Interpreter.jack index ff7a193..5735f5c 100644 --- a/alt/scheme/Interpreter.jack +++ b/alt/scheme/Interpreter.jack @@ -297,9 +297,12 @@ class Interpreter { if (opcode < 6) { if (opcode < 5) { // 4 - // TODO - let tty[0] = opcode; // DEBUG - do Intepreter.halt(); + // close :: x -- rib(x[0], stack, 1) + + // Note: modifyinging the top entry on the stack in place, + // but allocating a new rib for the closure. + let tmp = stack[0]; + let stack[0] = Interpreter.alloc(tmp[0], stack[1], 1); } else { // 5 From adae1f896811761f946a2868668f61a9f7ac48a8 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sat, 17 Feb 2024 22:17:50 -0500 Subject: [PATCH 140/221] =?UTF-8?q?Implement=20=E2=80=9Cpoke=E2=80=9D=20pr?= =?UTF-8?q?imitive=20(and=20the=20rest=20of=20the=20dispatching)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- alt/scheme/Interpreter.jack | 98 +++++++++++++++++++++++++++++++++++-- 1 file changed, 93 insertions(+), 5 deletions(-) diff --git a/alt/scheme/Interpreter.jack b/alt/scheme/Interpreter.jack index 5735f5c..db915f9 100644 --- a/alt/scheme/Interpreter.jack +++ b/alt/scheme/Interpreter.jack @@ -253,7 +253,7 @@ class Interpreter { if (opcode < 2) { if (opcode < 1) { // 0 - // rib: x y z -- rib(x, y, z) + // rib :: x y z -- rib(x, y, z) // Note: one rib becomes garbage (top entry of stack) let z = stack[0]; @@ -273,7 +273,7 @@ class Interpreter { } else { // 1 - // id: -- + // id :: x -- x // Literally nothing to see here } @@ -281,12 +281,16 @@ class Interpreter { else { // opcode >= 2 if (opcode < 3) { // 2 + // arg1 :: x y -- x (i.e. "drop") + // TODO let tty[0] = opcode; // DEBUG do Intepreter.halt(); } else { // 3 + // arg2 :: x y -- y + // TODO let tty[0] = opcode; // DEBUG do Intepreter.halt(); @@ -306,6 +310,8 @@ class Interpreter { } else { // 5 + // rib? :: x -- bool(x is a rib) + // TODO let tty[0] = opcode; // DEBUG do Intepreter.halt(); @@ -314,12 +320,16 @@ class Interpreter { else { // opcode >= 6 if (opcode < 7) { // 6 + // field0 :: rib(x, _, _) -- x + // TODO let tty[0] = opcode; // DEBUG do Intepreter.halt(); } else { // 7 + // field1 :: rib(_, y, _) -- y + // TODO let tty[0] = opcode; // DEBUG do Intepreter.halt(); @@ -332,12 +342,16 @@ class Interpreter { if (opcode < 10) { if (opcode < 9) { // 8 + // field2 :: rib(_, _, z) -- z + // TODO let tty[0] = opcode; // DEBUG do Intepreter.halt(); } else { // 9 + // field0-set! :: rib(_, y, z) x -- x (and update the rib in place: rib(x, y, z)) + // TODO let tty[0] = opcode; // DEBUG do Intepreter.halt(); @@ -346,12 +360,16 @@ class Interpreter { else { // opcode >= 10 if (opcode < 11) { // 10 + // field1-set! :: rib(x, _, z) y -- y (and update the rib in place: rib(x, y, z)) + // TODO let tty[0] = opcode; // DEBUG do Intepreter.halt(); } else { // 11 + // field2-set! :: rib(x, y, _) z -- z (and update the rib in place: rib(x, y, z)) + // TODO let tty[0] = opcode; // DEBUG do Intepreter.halt(); @@ -362,6 +380,8 @@ class Interpreter { if (opcode < 14) { if (opcode < 13) { // 12 + // eqv? :: x y -- bool(x is identical to y) + // TODO let tty[0] = opcode; // DEBUG do Intepreter.halt(); @@ -407,9 +427,77 @@ class Interpreter { } } } - else { - let tty[0] = opcode; // DEBUG - do Intepreter.halt(); + else { // opcode >= 16 + if (opcode < 24) { + if (opcode < 20) { + if (opcode < 18) { + if (opcode < 17) { + // 16 + // * :: x y -- (x * y) + + // TODO + let tty[0] = opcode; // DEBUG + do Intepreter.halt(); + } + else { + // 17 + // quotient :: not implemented + + do Intepreter.halt(); + } + } + else { // opcode >= 18 + if (opcode < 19) { + // 18 + // getchar :: -- (blocks until a key is pressed) + + // TODO + let tty[0] = opcode; // DEBUG + do Intepreter.halt(); + } + else { + // 19 + // putchar :: not implemented + + do Intepreter.halt(); + } + } + } + else { // opcode >= 20 + if (opcode < 22) { + if (opcode < 21) { + // 20 + // peek :: x -- RAM[x] + + // TODO + let tty[0] = opcode; // DEBUG + do Intepreter.halt(); + } + else { + // 21 + // poke :: x y -- y (and write the value y at RAM[x]) + + // Note: one rib becomes garbage: + let y = Interpreter.pop(); + + let x = stack[0]; + let tmp = 0; + let tmp[x] = y; + // Update the second stack entry in place + let stack[0] = y; + } + } + else { // opcode >= 22 + // 22 + // halt + + do Intepreter.halt(); + } + } + } + else { // opcode >= 24 + do Intepreter.halt(); + } } return; } From 232aae5fd09c89bec9e456cd90b4c3a7b68b1ff1 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sat, 17 Feb 2024 22:28:02 -0500 Subject: [PATCH 141/221] =?UTF-8?q?Implement=20=E2=80=9C*=E2=80=9D=20primi?= =?UTF-8?q?tive=20(for=20Jack)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using Math.multiply from the project 12 sollutions. --- alt/scheme/Interpreter.jack | 50 ++++++++++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/alt/scheme/Interpreter.jack b/alt/scheme/Interpreter.jack index db915f9..7c50139 100644 --- a/alt/scheme/Interpreter.jack +++ b/alt/scheme/Interpreter.jack @@ -435,9 +435,11 @@ class Interpreter { // 16 // * :: x y -- (x * y) - // TODO - let tty[0] = opcode; // DEBUG - do Intepreter.halt(); + // Note: one rib becomes garbage: + let y = Interpreter.pop(); + let x = stack[0]; + // Update second stack entry in place: + let stack[0] = Interpreter.multiply(x, y); } else { // 17 @@ -502,6 +504,48 @@ class Interpreter { return; } + /** + This was lifted verbatim from Math.jack in the solutions for project 12 from the book, except + for inlining abs(). + Does that mean it's good? Maybe, but it probably works. + */ + function int multiply(int x, int y) { + var boolean neg; + var int tmp; + var int sum, shiftedX, shiftedBit; + + // Get sign, then take absolute values (inline to save fn calls): + if ((x < 0) = (y < 0)) { let neg = false; } else { let neg = true; } + if (y < 0) { let y = -y; } + if (x < 0) { let x = -x; } + + // Put the smaller (abs.) value in y (because this is O(log y)): + if (x < y) { + let tmp = y; + let y = x; + let x = tmp; + } + + let sum = 0; + let shiftedX = x; + let shiftedBit = 1; + + while ((shiftedBit > 0) & ~(shiftedBit > y)) { + if (y & shiftedBit) { + let sum = sum + shiftedX; + } + let shiftedX = shiftedX + shiftedX; + let shiftedBit = shiftedBit + shiftedBit; + } + + // Check the original signs and adjust the result: + if (neg) { + return -sum; + } + else { + return sum; + } + } /** Allocate a rib on the heap, filling in the three fields. */ function Rib alloc(int x, int y, int z) { From dbd5466414d42c1d3e0fe783155f77a7faad492f Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sat, 17 Feb 2024 22:28:13 -0500 Subject: [PATCH 142/221] Fix comment --- nand/solutions/solved_12/Math.jack | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nand/solutions/solved_12/Math.jack b/nand/solutions/solved_12/Math.jack index e45e414..622636c 100644 --- a/nand/solutions/solved_12/Math.jack +++ b/nand/solutions/solved_12/Math.jack @@ -69,7 +69,7 @@ class Math { } /** Returns the integer part of x/y. - * When a Jack compiler detects the multiplication operator '/' in the + * When a Jack compiler detects the division operator '/' in the * program's code, it handles it by invoking this method. In other words, * the Jack expressions x/y and divide(x,y) return the same value. */ From 60ff33489bc7d2bd5d0720f3ba9e36ce83920190 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sat, 17 Feb 2024 22:30:15 -0500 Subject: [PATCH 143/221] =?UTF-8?q?Learn=20how=20to=20spell=20=E2=80=9CInt?= =?UTF-8?q?erpreter=E2=80=9D,=20maybe?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- alt/scheme/Interpreter.jack | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/alt/scheme/Interpreter.jack b/alt/scheme/Interpreter.jack index 7c50139..d99ff95 100644 --- a/alt/scheme/Interpreter.jack +++ b/alt/scheme/Interpreter.jack @@ -285,7 +285,7 @@ class Interpreter { // TODO let tty[0] = opcode; // DEBUG - do Intepreter.halt(); + do Interpreter.halt(); } else { // 3 @@ -293,7 +293,7 @@ class Interpreter { // TODO let tty[0] = opcode; // DEBUG - do Intepreter.halt(); + do Interpreter.halt(); } } } @@ -314,7 +314,7 @@ class Interpreter { // TODO let tty[0] = opcode; // DEBUG - do Intepreter.halt(); + do Interpreter.halt(); } } else { // opcode >= 6 @@ -324,7 +324,7 @@ class Interpreter { // TODO let tty[0] = opcode; // DEBUG - do Intepreter.halt(); + do Interpreter.halt(); } else { // 7 @@ -332,7 +332,7 @@ class Interpreter { // TODO let tty[0] = opcode; // DEBUG - do Intepreter.halt(); + do Interpreter.halt(); } } } @@ -346,7 +346,7 @@ class Interpreter { // TODO let tty[0] = opcode; // DEBUG - do Intepreter.halt(); + do Interpreter.halt(); } else { // 9 @@ -354,7 +354,7 @@ class Interpreter { // TODO let tty[0] = opcode; // DEBUG - do Intepreter.halt(); + do Interpreter.halt(); } } else { // opcode >= 10 @@ -364,7 +364,7 @@ class Interpreter { // TODO let tty[0] = opcode; // DEBUG - do Intepreter.halt(); + do Interpreter.halt(); } else { // 11 @@ -372,7 +372,7 @@ class Interpreter { // TODO let tty[0] = opcode; // DEBUG - do Intepreter.halt(); + do Interpreter.halt(); } } } @@ -384,7 +384,7 @@ class Interpreter { // TODO let tty[0] = opcode; // DEBUG - do Intepreter.halt(); + do Interpreter.halt(); } else { // 13 @@ -445,7 +445,7 @@ class Interpreter { // 17 // quotient :: not implemented - do Intepreter.halt(); + do Interpreter.halt(); } } else { // opcode >= 18 @@ -455,13 +455,13 @@ class Interpreter { // TODO let tty[0] = opcode; // DEBUG - do Intepreter.halt(); + do Interpreter.halt(); } else { // 19 // putchar :: not implemented - do Intepreter.halt(); + do Interpreter.halt(); } } } @@ -473,7 +473,7 @@ class Interpreter { // TODO let tty[0] = opcode; // DEBUG - do Intepreter.halt(); + do Interpreter.halt(); } else { // 21 @@ -493,12 +493,12 @@ class Interpreter { // 22 // halt - do Intepreter.halt(); + do Interpreter.halt(); } } } else { // opcode >= 24 - do Intepreter.halt(); + do Interpreter.halt(); } } return; From 4e58d59c104e9eb5e8c7cb0862e23f11189d6357 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sat, 17 Feb 2024 22:35:12 -0500 Subject: [PATCH 144/221] Implement field0/1/2(_set!) primitives --- alt/scheme/Interpreter.jack | 48 +++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/alt/scheme/Interpreter.jack b/alt/scheme/Interpreter.jack index d99ff95..e5d1094 100644 --- a/alt/scheme/Interpreter.jack +++ b/alt/scheme/Interpreter.jack @@ -322,17 +322,17 @@ class Interpreter { // 6 // field0 :: rib(x, _, _) -- x - // TODO - let tty[0] = opcode; // DEBUG - do Interpreter.halt(); + // No allocation: the top entry on the stack is updated in place + let tmp = stack[0]; + let stack[0] = tmp[0]; } else { // 7 // field1 :: rib(_, y, _) -- y - // TODO - let tty[0] = opcode; // DEBUG - do Interpreter.halt(); + // No allocation: the top entry on the stack is updated in place + let tmp = stack[0]; + let stack[0] = tmp[1]; } } } @@ -344,17 +344,21 @@ class Interpreter { // 8 // field2 :: rib(_, _, z) -- z - // TODO - let tty[0] = opcode; // DEBUG - do Interpreter.halt(); + // No allocation: the top entry on the stack is updated in place + let tmp = stack[0]; + let stack[0] = tmp[2]; } else { // 9 // field0-set! :: rib(_, y, z) x -- x (and update the rib in place: rib(x, y, z)) - // TODO - let tty[0] = opcode; // DEBUG - do Interpreter.halt(); + // Note: one rib becomes garbage (top entry of stack) + + let x = Interpreter.pop(); + let tmp = stack[0]; + let tmp[0] = x; + // Update the second entry on the stack in place: + let stack[0] = x; } } else { // opcode >= 10 @@ -362,17 +366,25 @@ class Interpreter { // 10 // field1-set! :: rib(x, _, z) y -- y (and update the rib in place: rib(x, y, z)) - // TODO - let tty[0] = opcode; // DEBUG - do Interpreter.halt(); + // Note: one rib becomes garbage (top entry of stack) + + let y = Interpreter.pop(); + let tmp = stack[0]; + let tmp[1] = y; + // Update the second entry on the stack in place: + let stack[0] = y; } else { // 11 // field2-set! :: rib(x, y, _) z -- z (and update the rib in place: rib(x, y, z)) - // TODO - let tty[0] = opcode; // DEBUG - do Interpreter.halt(); + // Note: one rib becomes garbage (top entry of stack) + + let z = Interpreter.pop(); + let tmp = stack[0]; + let tmp[2] = z; + // Update the second entry on the stack in place: + let stack[0] = z; } } } From 456c8f08d03545917f09f3c195a72ab8b81fa526 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sat, 17 Feb 2024 22:37:33 -0500 Subject: [PATCH 145/221] =?UTF-8?q?Implement=20=E2=80=9Ceqv=3F=E2=80=9D=20?= =?UTF-8?q?primitive?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- alt/scheme/Interpreter.jack | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/alt/scheme/Interpreter.jack b/alt/scheme/Interpreter.jack index e5d1094..b8b9bd5 100644 --- a/alt/scheme/Interpreter.jack +++ b/alt/scheme/Interpreter.jack @@ -394,9 +394,16 @@ class Interpreter { // 12 // eqv? :: x y -- bool(x is identical to y) - // TODO - let tty[0] = opcode; // DEBUG - do Interpreter.halt(); + // Note: one rib becomes garbage (top entry of stack) + + let y = Interpreter.pop(); + let x = stack[0]; + if (x = y) { + let stack[0] = ribTrue; + } + else { + let stack[0] = ribFalse; + } } else { // 13 From fe11b2163d9f586f1a25db851a8082121bd80b4a Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sat, 17 Feb 2024 22:41:36 -0500 Subject: [PATCH 146/221] =?UTF-8?q?Implement=20=E2=80=9Crib=3F=E2=80=9D=20?= =?UTF-8?q?primitive?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- alt/scheme/Interpreter.jack | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/alt/scheme/Interpreter.jack b/alt/scheme/Interpreter.jack index b8b9bd5..d16e9a0 100644 --- a/alt/scheme/Interpreter.jack +++ b/alt/scheme/Interpreter.jack @@ -312,9 +312,13 @@ class Interpreter { // 5 // rib? :: x -- bool(x is a rib) - // TODO - let tty[0] = opcode; // DEBUG - do Interpreter.halt(); + let tmp = stack[0]; + if (Rib.isRib(tmp)) { + let stack[0] = ribTrue; + } + else { + let stack[0] = ribFalse; + } } } else { // opcode >= 6 From ee0d921d24c55f57305e0c490a552181bc1b4b4f Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sat, 17 Feb 2024 22:41:53 -0500 Subject: [PATCH 147/221] =?UTF-8?q?Implement=20=E2=80=9Carg1=E2=80=9D=20pr?= =?UTF-8?q?imitive?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- alt/scheme/Interpreter.jack | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/alt/scheme/Interpreter.jack b/alt/scheme/Interpreter.jack index d16e9a0..7f21375 100644 --- a/alt/scheme/Interpreter.jack +++ b/alt/scheme/Interpreter.jack @@ -283,9 +283,7 @@ class Interpreter { // 2 // arg1 :: x y -- x (i.e. "drop") - // TODO - let tty[0] = opcode; // DEBUG - do Interpreter.halt(); + let stack = stack[1]; } else { // 3 From 4e9649fd5de86cdb86580018530b9e6a322a0fce Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 18 Feb 2024 21:29:33 -0500 Subject: [PATCH 148/221] Fix rendering ofcontinuations on the stack to be usefuil --- alt/scheme/inspector.py | 53 +++++++++++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 15 deletions(-) diff --git a/alt/scheme/inspector.py b/alt/scheme/inspector.py index 256c82e..5e63238 100644 --- a/alt/scheme/inspector.py +++ b/alt/scheme/inspector.py @@ -3,6 +3,8 @@ from nand.vector import extend_sign, unsigned from alt import big +# Seems like overkill +SHOW_SAVED_STACKS = False class Inspector: def __init__(self, computer, symbols, stack_loc, rom_base=big.ROM_BASE, rom_limit=big.HEAP_BASE): @@ -96,7 +98,7 @@ def _obj(self, val): return f"proc({PRIMITIVES[x]})" else: num_args, instr = self.peek(x), self.peek(x+2) - return f"proc(args={num_args}, env={self.show_obj(y)}, instr={self.show_addr(instr)}){self.show_addr(val)}" + return f"proc(args={num_args}, env={list_str(self.stack(y))}, instr={self.show_addr(instr)}){self.show_addr(val)}" elif z == 2: # symbol return f"symbol({self._obj(y)} = {self._obj(x)})" elif z == 3: # string @@ -126,25 +128,38 @@ def show_obj(self, val): return str(self._obj(val)) - def stack(self): + def stack(self, addr=None): """Contents of the stack, bottom to top.""" + if addr is None: + addr = self.peek(self.stack_loc) - def go(addr): - if self.symbols_by_addr.get(addr) == "rib_nil": - # This appears only after the outer continuation is invoked: - return [] - elif addr == 0: - # sanity check - raise Exception("Unexpected zero pointer in stack") + if self.symbols_by_addr.get(addr) == "rib_nil": + # This appears only after the outer continuation is invoked: + return [] + elif addr == 0: + # sanity check + return ["<0>!"] + else: + x, y, z = self.peek(addr), self.peek(addr+1), self.peek(addr+2) + + if z == 0: # pair + return self.stack(y) + [self._obj(x)] else: - x, y, z = self.peek(addr), self.peek(addr+1), self.peek(addr+2) + if SHOW_SAVED_STACKS: + saved_str = list_str(self.stack(x)) + cont = f"cont(saved={saved_str}; {self.show_addr(z)}){self.show_addr(addr)}" + else: + cont = f"cont({self.show_addr(z)}){self.show_addr(addr)}" - if z == 0: # pair - return go(y) + [self._obj(x)] + if y == 0: + # outer continuation: there is no proc, and no further stack + return [cont] else: - # A continuation: (stack, closure, next instr) - return go(x) + [f"cont(saved={self._obj(x)}; {self._obj(y)}; {self.show_addr(z)}){self.show_addr(addr)}"] - return go(self.peek(self.stack_loc)) + # Note: no instruction ever actually looks at this entry. It's just holding `env`, + # which we're about to traverse. But need to show something so slot numbers make + # sense. + closure = "" + return self.stack(self.peek(y+1)) + [closure, cont] def show_stack(self): @@ -154,6 +169,14 @@ def show_stack(self): return ", ".join(str(o) for o in self.stack()) +def list_str(lst): + """Looks like a list's repr(), but use str() on the elements to avoid quoting everything. + + Why do I have to write this? Or do I? + """ + return "[" + ", ".join(str(x) for x in lst) + "]" + + PRIMITIVES = { 0: "rib", 1: "id", From d70b8f09f97ff806778ae15162e5630d37230c7d Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 18 Feb 2024 21:30:34 -0500 Subject: [PATCH 149/221] Make instruction tracing in tests more useful --- alt/scheme/test_rvm.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/alt/scheme/test_rvm.py b/alt/scheme/test_rvm.py index 28ee812..fc874c6 100755 --- a/alt/scheme/test_rvm.py +++ b/alt/scheme/test_rvm.py @@ -9,6 +9,10 @@ from nand.translate import AssemblySource from nand.vector import unsigned + +SHOW_ALL_LABELS = False + + # TODO: put this somewhere common: def parameterize(f): def vector(chip, **args): @@ -403,12 +407,14 @@ def run_to_halt(program, interpreter, max_cycles=20000, simulator="codegen"): output.append(tty_char) if computer.fetch and inspect.is_labeled(computer.pc): - cpu_pc = inspect.show_addr(computer.pc) - print(f"{cpu_pc}") + if SHOW_ALL_LABELS: + print(f"{inspect.show_addr(computer.pc)}") if computer.pc == interp_loop_addr: + print(f"{cycles:,d}") stack = ", ".join(str(x) for x in inspect.stack()) print(f" stack: {stack}") pc = inspect.peek(pc_loc) - print(f" {inspect.show_addr(pc):<10} {inspect.show_instr(pc)}") + print(f" pc: {inspect.show_addr(pc)}") + print(f" {inspect.show_instr(pc)}") return (inspect, output) From e0233955134aea76ed0c0d5363b4f6a13531b359 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 18 Feb 2024 21:31:10 -0500 Subject: [PATCH 150/221] Increase max_cycles for the Jack interpreter; and fail more usefully --- alt/scheme/test_rvm.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/alt/scheme/test_rvm.py b/alt/scheme/test_rvm.py index fc874c6..dcfeccd 100755 --- a/alt/scheme/test_rvm.py +++ b/alt/scheme/test_rvm.py @@ -20,7 +20,7 @@ def vector(chip, **args): def codegen(chip, **args): return run_to_halt(chip, simulator="codegen", interpreter="assembly", **args) def jack(chip, **args): - return run_to_halt(chip, simulator="codegen", interpreter="jack", **args) + return run_to_halt(chip, simulator="codegen", interpreter="jack", max_cycles=100000, **args) return pytest.mark.parametrize("run", [vector, codegen, jack])(f) @parameterize @@ -397,7 +397,10 @@ def run_to_halt(program, interpreter, max_cycles=20000, simulator="codegen"): inspect = Inspector(computer, symbols, stack_loc) - while (not computer.fetch or computer.pc != halt_loop_addr) and cycles <= max_cycles: + while not (computer.fetch and computer.pc == halt_loop_addr): + if cycles > max_cycles: + raise Exception(f"exceeded max_cycles: {max_cycles:,d}") + computer.ticktock() cycles += 1 From dd55159240611db75ced717f59fe42e48892aa68 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Mon, 19 Feb 2024 00:19:31 -0500 Subject: [PATCH 151/221] Finally get around to optimizing leaf functions --- alt/README.md | 2 +- alt/reg.py | 221 ++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 172 insertions(+), 51 deletions(-) diff --git a/alt/README.md b/alt/README.md index e40488f..801ab3c 100644 --- a/alt/README.md +++ b/alt/README.md @@ -54,7 +54,7 @@ replaces certain function calls with lower-overhead "reduced" alternatives. | [alt/eight.py](eight.py) | 1,032 (-18%) | _same_ | +100% | +100% | | [alt/big.py](big.py) | 1,448 (+14%) | ? | ? | ? | | [alt/lazy.py](lazy.py) | _same_ | 23,650 (-8%) | 37,300 (-10%) | 111,000 (-14%) | -| [alt/reg.py](reg.py) | _same_ | 20,900 (-19%) | 19,150 (-54%) | 59,000 (-54%) | +| [alt/reg.py](reg.py) | _same_ | 20,300 (-21%) | 15,600 (-62%) | 59,000 (-54%) | | [alt/reduce.py](reduce.py) | _same_ | 27,350 (+6.5%) | 20,300 (-51%) | _same_ | diff --git a/alt/reg.py b/alt/reg.py index 5568268..7aa808f 100755 --- a/alt/reg.py +++ b/alt/reg.py @@ -30,7 +30,7 @@ Other differences: -- Subroutine results are stored in r0 (aka @5) +- Subroutine results are stored in @12 (aka r7) - Return addresses are stored at @4 (aka THAT) - Each call site just pushes arguments, stashes the return address and then jumps to the "function" address. It's up to each function to do any saving of registers and adjustment of the stack @@ -52,6 +52,15 @@ from nand.solutions.solved_11 import SymbolTable, VarKind +OPTIMIZE_LEAF_FUNCTIONS = True +"""If True, a simpler call/return sequence is used for functions that do not need to manipulate the +stack (because they don't call any subroutines). +Saves ~30 instructions per leaf function in ROM, plus another ~30 at runtime. For small functions like +Math.min/max/abs, this might save ~50% of the space and ~70% of the time. +Possibly makes tracing/debugging confusing or useless in these functions. +""" + + # IR # # A simplified AST for statements and expressions: @@ -1004,6 +1013,11 @@ def __init__(self, asm=None): self.asm = asm if asm else AssemblySource() solved_07.Translator.__init__(self, self.asm) + # HACK: stash the leaf-ishness of the current subroutine statefully, so we don't have to + # thread it through the traversal. When we're dealing with a leaf, this is the number of + # args. Otherwise, None. + self.leaf_sub_args = None + # self.preamble() # called by the loader, apparently def handle(self, op): @@ -1032,48 +1046,93 @@ def translate_subroutine(self, subroutine_ast: Subroutine, class_name: str): self.asm.start(f"function {class_name}.{subroutine_ast.name} {subroutine_ast.num_vars}") self.asm.label(f"{self.function_namespace}") - # Note: this could be done with a common sequence in the preamble, but jumping there and - # back would cost at least 6 cycles or so, and the goal here is to get small by generating - # tighter code, not save space in ROM by adding runtime overhead. - # TODO: avoid this overhead entirely for leaf functions, by just not adjusting the stack - # at all. - self.asm.comment("push the return address, then LCL and ARG") - self.asm.instr(f"@{RETURN_ADDRESS}") - self.asm.instr("D=M") - self._push_d() - self.asm.instr("@LCL") - self.asm.instr("D=M") - self._push_d() - self.asm.instr("@ARG") - self.asm.instr("D=M") - self._push_d() + def uses_stack(stmt: Stmt): + if isinstance(stmt, Eval): + return isinstance(stmt.expr, CallSub) + elif isinstance(stmt, IndirectWrite): + return False + elif isinstance(stmt, Store): + return False + elif isinstance(stmt, If): + return any(uses_stack(s) for s in stmt.when_true + (stmt.when_false or [])) + elif isinstance(stmt, While): + return any(uses_stack(s) for s in stmt.test + stmt.body) + elif isinstance(stmt, Return): + return isinstance(stmt.expr, CallSub) # FIXME: does this happen? + elif isinstance(stmt, Push): + return True + elif isinstance(stmt, Discard): + return isinstance(stmt.expr, CallSub) + else: + raise Exception(f"Unknown Stmt: {stmt}") + + is_leaf = OPTIMIZE_LEAF_FUNCTIONS and not any(uses_stack(s) for s in subroutine_ast.body) + + if is_leaf: + # TODO: if solved_07.INITIALIZE_LOCALS? + if subroutine_ast.num_vars > 0: + # Note: this is probably pretty rare. You need to have a leaf function with more + # live variables than there are registers (typically 8). + self.asm.comment(f"initialize locals ({subroutine_ast.num_vars})") + self.asm.instr("@SP") + self.asm.instr("M=0") + for _ in range(subroutine_ast.num_vars-1): + self.asm.instr("A=A+1") + self.asm.instr("M=0") + else: + self.asm.comment("no locals to initialize (leaf function), so no-op") + # Bogus: so the function label and the first stmt don't end up at the same address + self.asm.instr("D=D") - self.asm.comment("LCL = SP") - self.asm.instr("@SP") - self.asm.instr("D=M") - self.asm.instr("@LCL") - self.asm.instr("M=D") + else: + # Note: this could be done with a common sequence in the preamble, but jumping there and + # back would cost at least 6 cycles or so, and the goal here is to get small by generating + # tighter code, not save space in ROM by adding runtime overhead. + # TODO: avoid this overhead entirely for leaf functions, by just not adjusting the stack + # at all. + self.asm.comment("push the return address, then LCL and ARG") + self.asm.instr(f"@{RETURN_ADDRESS}") + self.asm.instr("D=M") + self._push_d() + self.asm.instr("@LCL") + self.asm.instr("D=M") + self._push_d() + self.asm.instr("@ARG") + self.asm.instr("D=M") + self._push_d() - self.asm.comment("ARG = SP - (num_args + 3)") - self.asm.instr(f"@{subroutine_ast.num_args + 3}") - self.asm.instr("D=A") - self.asm.instr("@SP") - self.asm.instr("D=M-D") - self.asm.instr("@ARG") - self.asm.instr("M=D") + self.asm.comment("LCL = SP") + self.asm.instr("@SP") + self.asm.instr("D=M") + self.asm.instr("@LCL") + self.asm.instr("M=D") + + self.asm.comment("ARG = SP - (num_args + 3)") + self.asm.instr(f"@{subroutine_ast.num_args + 3}") + self.asm.instr("D=A") + self.asm.instr("@SP") + self.asm.instr("D=M-D") + self.asm.instr("@ARG") + self.asm.instr("M=D") - if subroutine_ast.num_vars > 0: - self.asm.comment(f"space for locals ({subroutine_ast.num_vars})") - self.reserve_local_space(subroutine_ast.num_vars) + if subroutine_ast.num_vars > 0: + self.asm.comment(f"space for locals ({subroutine_ast.num_vars})") + self.reserve_local_space(subroutine_ast.num_vars) # Now the body: + if is_leaf: + self.leaf_sub_args = subroutine_ast.num_args + else: + self.leaf_sub_args = None + for s in subroutine_ast.body: self._handle(s) + self.asm.blank() instr_count_after = self.asm.instruction_count - # print(f"Translated {class_name}.{subroutine_ast.name}; instructions: {instr_count_after - instr_count_before:,d}") + # print(f"Translated {class_name}.{subroutine_ast.name}; instructions: {instr_count_after - instr_count_before:,d}\n") # DEBUG # Statements: @@ -1163,12 +1222,22 @@ def handle_Store(self, ast: Store): raise Exception(f"should have been rewritten: {ast}") else: - if kind == "argument": - segment_ptr = "ARG" - elif kind == "local": - segment_ptr = "LCL" + if self.leaf_sub_args is not None: + # LCL and ARG are not used; just index off of SP (below for args, above for locals) + segment_ptr = "SP" + if kind == "argument": + index = -self.leaf_sub_args + ast.location.idx + elif kind == "local": + index = ast.location.idx + else: + raise Exception(f"Unknown location: {ast}") else: - raise Exception(f"Unknown location: {ast}") + if kind == "argument": + segment_ptr = "ARG" + elif kind == "local": + segment_ptr = "LCL" + else: + raise Exception(f"Unknown location: {ast}") # Super common case: initialize a var to 0 or 1 (or -1): if imm is not None: @@ -1180,15 +1249,25 @@ def handle_Store(self, ast: Store): self.asm.instr(f"@{segment_ptr}") self.asm.instr("A=M+1") self.asm.instr(f"M={imm}") - else: + elif index > 0: self.asm.instr(f"@{index}") self.asm.instr("D=A") self.asm.instr(f"@{segment_ptr}") self.asm.instr("A=M+D") self.asm.instr(f"M={imm}") + elif index == -1: + self.asm.instr(f"@{segment_ptr}") + self.asm.instr("A=M-1") + self.asm.instr(f"M={imm}") + else: + self.asm.instr(f"@{-index}") + self.asm.instr("D=A") + self.asm.instr(f"@{segment_ptr}") + self.asm.instr("A=M-D") + self.asm.instr(f"M={imm}") else: - if index <= 6: + if 0 <= index <= 6: self._handle(ast.value) self.asm.instr(f"@{segment_ptr}") if index == 0: @@ -1200,8 +1279,12 @@ def handle_Store(self, ast: Store): self.asm.instr("M=D") else: - self.asm.instr(f"@{index}") - self.asm.instr("D=A") + if index > 0: + self.asm.instr(f"@{index}") + self.asm.instr("D=A") + else: + self.asm.instr(f"@{-index}") + self.asm.instr("D=-A") self.asm.instr(f"@{segment_ptr}") self.asm.instr("D=M+D") self.asm.instr("@R15") # code smell: needing R15 shows that this isn't actually atomic @@ -1295,7 +1378,21 @@ def handle_Return(self, ast: Return): self.asm.instr(f"@{RESULT}") self.asm.instr("M=D") - self.return_op() + if self.leaf_sub_args is not None: + self.asm.start("return (from leaf)") + if self.leaf_sub_args > 0: + # TODO: special-case 1 and 2 arguments to save 2/1 instructions + self.asm.comment("pop arguments") + self.asm.instr(f"@{self.leaf_sub_args}") + self.asm.instr("D=A") + self.asm.instr("@SP") + self.asm.instr("M=M-D") + self.asm.instr(f"@{RETURN_ADDRESS}") + self.asm.instr("A=M") + self.asm.instr("0;JMP") + + else: + self.return_op() def handle_Push(self, ast): if not isinstance(ast.expr, CallSub): @@ -1393,12 +1490,22 @@ def handle_Location(self, ast: Location): elif ast.kind == "field": raise Exception(f"should have been rewritten: {ast}") else: - if ast.kind == "argument": - segment_ptr = "ARG" - elif ast.kind == "local": - segment_ptr = "LCL" + if self.leaf_sub_args is not None: + # LCL and ARG are not used; just index off of SP (below for args, above for locals) + segment_ptr = "SP" + if ast.kind == "argument": + index = -self.leaf_sub_args + ast.idx + elif ast.kind == "local": + index = ast.index + else: + raise Exception(f"Unknown location: {ast}") else: - raise Exception(f"Unknown location: {ast}") + if ast.kind == "argument": + segment_ptr = "ARG" + elif ast.kind == "local": + segment_ptr = "LCL" + else: + raise Exception(f"Unknown location: {ast}") if index == 0: self.asm.instr(f"@{segment_ptr}") @@ -1410,13 +1517,26 @@ def handle_Location(self, ast: Location): self.asm.instr(f"@{segment_ptr}") self.asm.instr("A=M+1") self.asm.instr("A=A+1") - else: + elif index > 0: self.asm.instr(f"@{index}") self.asm.instr("D=A") self.asm.instr(f"@{segment_ptr}") - self.asm.instr("A=D+M") + self.asm.instr("A=M+D") + elif index == -1: + self.asm.instr(f"@{segment_ptr}") + self.asm.instr("A=M-1") + elif index == -2: + self.asm.instr(f"@{segment_ptr}") + self.asm.instr("A=M-1") + self.asm.instr("A=A-1") + else: + self.asm.instr(f"@{-index}") + self.asm.instr("D=A") + self.asm.instr(f"@{segment_ptr}") + self.asm.instr("A=M-D") self.asm.instr("D=M") + def handle_Reg(self, ast: Reg): self.asm.instr(f"@R{5+ast.idx}") self.asm.instr("D=M") @@ -1600,7 +1720,6 @@ def _compare(self, op): def _return(self): "Override the normal sequence; much less stack adjustment required." - # TODO: use this only for non-leaf subroutines label = self.asm.next_label("return_common") @@ -1643,6 +1762,8 @@ def _return(self): self.asm.instr("A=M") self.asm.instr("0;JMP") + self.asm.blank() + return label From cb25a13d17455d46c27d15f5ed95a9cb0690f9c1 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Mon, 19 Feb 2024 10:49:22 -0500 Subject: [PATCH 152/221] Collapse simple expressions into Store(static) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No need for a temporary when the store doesn’t need to compute the destination address --- alt/README.md | 2 +- alt/reg.py | 72 ++++++++++++++++++++++++++++++++++++--------------- 2 files changed, 52 insertions(+), 22 deletions(-) diff --git a/alt/README.md b/alt/README.md index 801ab3c..999190f 100644 --- a/alt/README.md +++ b/alt/README.md @@ -54,7 +54,7 @@ replaces certain function calls with lower-overhead "reduced" alternatives. | [alt/eight.py](eight.py) | 1,032 (-18%) | _same_ | +100% | +100% | | [alt/big.py](big.py) | 1,448 (+14%) | ? | ? | ? | | [alt/lazy.py](lazy.py) | _same_ | 23,650 (-8%) | 37,300 (-10%) | 111,000 (-14%) | -| [alt/reg.py](reg.py) | _same_ | 20,300 (-21%) | 15,600 (-62%) | 59,000 (-54%) | +| [alt/reg.py](reg.py) | _same_ | 20,300 (-21%) | 15,700 (-62%) | 58,800 (-54%) | | [alt/reduce.py](reduce.py) | _same_ | 27,350 (+6.5%) | 20,300 (-51%) | _same_ | diff --git a/alt/reg.py b/alt/reg.py index 7aa808f..9b204d4 100755 --- a/alt/reg.py +++ b/alt/reg.py @@ -94,9 +94,18 @@ class IndirectWrite(NamedTuple): value: "Value" class Store(NamedTuple): - """Store a value to a location identified by segment and index.""" + """Store a value to a location identified by segment and index. + + Restrictions on the RHS expr: + - if location is not "static", then expr is always a Value, because the destination address + may need to be computed. + - if location is "static", then expr can be any flattened expression + + And yes, this does imply that storing to a static and storing to argument/local are + actually different operations worthy of their own Stmt types. + """ location: "Location" - value: "Value" + expr: "Expr" Cmp = str # requires 3.8: Literal["!="] # Meaning "non-zero"; TODO: the rest of the codes @@ -144,9 +153,10 @@ class Local(NamedTuple): class Location(NamedTuple): """A location identified by segment and index.""" + # TODO: break this out into StackVar and Static kind: VarKind - idx: int - name: str # include for debugging purposes + idx: int # Used for argument/local + name: str # Used for static class Reg(NamedTuple): """A variable which is local the the subroutine scope, does not need to persist @@ -236,6 +246,12 @@ def flatten_statement(stmt: jack_ast.Statement) -> List[Stmt]: expr_stmts, expr = flatten_expression(stmt.expr, force=False) let_stmt = Eval(dest=loc, expr=expr) return expr_stmts + [let_stmt] + elif loc.kind == "static": + # Note: writing to a static is simple (no need to generate target address), so the + # RHS doesn't need to be in a register. + expr_stmts, expr = flatten_expression(stmt.expr, force=False) + let_stmt = Store(loc, expr) + return expr_stmts + [let_stmt] elif loc.kind == "this": if isinstance(this_expr, Local): this_var = this_expr @@ -574,7 +590,7 @@ def analyze_while(stmt: While, live_at_end) -> Tuple[While, Set[str]]: read.update(refs(stmt.address)) read.update(refs(stmt.value)) elif isinstance(stmt, Store): - read.update(refs(stmt.value)) + read.update(refs(stmt.expr)) elif isinstance(stmt, If): # Note: overwriting stmt (and live_set) here: stmt, live_set = analyze_if(stmt, live_set) @@ -703,8 +719,8 @@ def rewrite_statement(stmt: Stmt) -> List[Stmt]: value_stmts, value_value = rewrite_value(stmt.value) return address_stmts + value_stmts + [IndirectWrite(address_value, value_value)] elif isinstance(stmt, Store): - value_stmts, value = rewrite_expr(stmt.value) - return value_stmts + [Store(stmt.location, value)] + expr_stmts, expr = rewrite_expr(stmt.expr) + return expr_stmts + [Store(stmt.location, expr)] elif isinstance(stmt, If): value_stmts, value = rewrite_expr(stmt.value) when_true = rewrite_statements(stmt.when_true) @@ -888,9 +904,8 @@ def rewrite_statement(stmt: Stmt) -> Stmt: value = rewrite_value(stmt.value) return IndirectWrite(address, value) elif isinstance(stmt, Store): - # print(f"rewrite Store: {stmt}") - value = rewrite_value(stmt.value) - return Store(stmt.location, value) + expr = rewrite_expr(stmt.expr) + return Store(stmt.location, expr) elif isinstance(stmt, If): value = rewrite_value(stmt.value) when_true = rewrite_statements(stmt.when_true) @@ -1052,7 +1067,7 @@ def uses_stack(stmt: Stmt): elif isinstance(stmt, IndirectWrite): return False elif isinstance(stmt, Store): - return False + return isinstance(stmt.expr, CallSub) elif isinstance(stmt, If): return any(uses_stack(s) for s in stmt.when_true + (stmt.when_false or [])) elif isinstance(stmt, While): @@ -1203,9 +1218,10 @@ def handle_IndirectWrite(self, ast: IndirectWrite): self.asm.instr("M=D") def handle_Store(self, ast: Store): - self.asm.start(f"store {_Stmt_str(ast)}") + if not isinstance(ast.expr, CallSub): + self.asm.start(f"store-{self.describe_expr(ast.expr)} {_Stmt_str(ast)}") - imm = self.immediate(ast.value) + imm = self.immediate(ast.expr) kind, index = ast.location.kind, ast.location.idx if kind == "static": @@ -1214,7 +1230,11 @@ def handle_Store(self, ast: Store): self.asm.instr(f"@{symbol_name}") self.asm.instr(f"M={imm}") else: - self._handle(ast.value) + self._handle(ast.expr) + + if isinstance(ast.expr, CallSub): + self.asm.start(f"store-result {_Expr_str(ast.location)} = ") + self.asm.instr(f"@{symbol_name}") self.asm.instr("M=D") @@ -1249,16 +1269,16 @@ def handle_Store(self, ast: Store): self.asm.instr(f"@{segment_ptr}") self.asm.instr("A=M+1") self.asm.instr(f"M={imm}") + elif index == -1: + self.asm.instr(f"@{segment_ptr}") + self.asm.instr("A=M-1") + self.asm.instr(f"M={imm}") elif index > 0: self.asm.instr(f"@{index}") self.asm.instr("D=A") self.asm.instr(f"@{segment_ptr}") self.asm.instr("A=M+D") self.asm.instr(f"M={imm}") - elif index == -1: - self.asm.instr(f"@{segment_ptr}") - self.asm.instr("A=M-1") - self.asm.instr(f"M={imm}") else: self.asm.instr(f"@{-index}") self.asm.instr("D=A") @@ -1267,8 +1287,14 @@ def handle_Store(self, ast: Store): self.asm.instr(f"M={imm}") else: + # For small index, compute the destination address without clobbering D: + # Note: not optimizing negative indexes because we rarely see store to argument if 0 <= index <= 6: - self._handle(ast.value) + self._handle(ast.expr) + + if isinstance(ast.expr, CallSub): + self.asm.start(f"store-result {_Expr_str(ast.location)} = ") + self.asm.instr(f"@{segment_ptr}") if index == 0: self.asm.instr("A=M") @@ -1289,7 +1315,11 @@ def handle_Store(self, ast: Store): self.asm.instr("D=M+D") self.asm.instr("@R15") # code smell: needing R15 shows that this isn't actually atomic self.asm.instr("M=D") - self._handle(ast.value) + self._handle(ast.expr) + + if isinstance(ast.expr, CallSub): + self.asm.start(f"store-result {_Expr_str(ast.location)} = ") + self.asm.instr("@R15") self.asm.instr("A=M") self.asm.instr("M=D") @@ -1785,7 +1815,7 @@ def _Stmt_str(stmt: Stmt) -> str: elif isinstance(stmt, IndirectWrite): return f"mem[{_Expr_str(stmt.address)}] = {_Expr_str(stmt.value)}" elif isinstance(stmt, Store): - return f"{_Expr_str(stmt.location)} = {_Expr_str(stmt.value)}" + return f"{_Expr_str(stmt.location)} = {_Expr_str(stmt.expr)}" elif isinstance(stmt, If): return "\n".join([ f"if ({_Expr_str(stmt.value)} {stmt.cmp} zero)", From 1fdf66ec9da9b07b08d166ceb0682bd5395ea97d Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Mon, 19 Feb 2024 11:59:43 -0500 Subject: [PATCH 153/221] =?UTF-8?q?Include=20Static=20ref=20in=20Value;=20?= =?UTF-8?q?they=20don=E2=80=99t=20need=20to=20be=20flattened?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- alt/reg.py | 75 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 47 insertions(+), 28 deletions(-) diff --git a/alt/reg.py b/alt/reg.py index 9b204d4..d0ea105 100755 --- a/alt/reg.py +++ b/alt/reg.py @@ -85,7 +85,7 @@ class Eval(NamedTuple): """Evaluate an expression and store the result somewhere.""" - dest: "Local" + dest: "Local" # Actually Reg (or Static?) after rewriting expr: "Expr" class IndirectWrite(NamedTuple): @@ -97,14 +97,14 @@ class Store(NamedTuple): """Store a value to a location identified by segment and index. Restrictions on the RHS expr: - - if location is not "static", then expr is always a Value, because the destination address + - if location is not Static, then expr is always a Value, because the destination address may need to be computed. - - if location is "static", then expr can be any flattened expression + - if location is Static, then expr can be any flattened expression And yes, this does imply that storing to a static and storing to argument/local are actually different operations worthy of their own Stmt types. """ - location: "Location" + location: Union["Location", "Static"] expr: "Expr" Cmp = str # requires 3.8: Literal["!="] # Meaning "non-zero"; TODO: the rest of the codes @@ -152,11 +152,10 @@ class Local(NamedTuple): name: str # TODO: parameterize, so we can annotate, etc.? class Location(NamedTuple): - """A location identified by segment and index.""" - # TODO: break this out into StackVar and Static + """A location identified by segment (argument/local) and index.""" kind: VarKind - idx: int # Used for argument/local - name: str # Used for static + idx: int + name: str # For debugging class Reg(NamedTuple): """A variable which is local the the subroutine scope, does not need to persist @@ -164,7 +163,11 @@ class Reg(NamedTuple): idx: int name: str # include for debugging purposes -Value = Union[Const, Local, Reg] +class Static(NamedTuple): + """A static variable; just as efficient to access as Reg.""" + name: str + +Value = Union[Const, Local, Reg, Static] """A value that's eligible to be referenced in any statement or expression.""" class Binary(NamedTuple): @@ -228,12 +231,14 @@ def next_var(name: Optional[str] = None) -> Local: elif ast.kind == "constructor": this_expr = next_var("this") - def resolve_name(name: str) -> Union[Local, Location]: + def resolve_name(name: str) -> Union[Local, Location, Static]: # TODO: this makes sense for locals, arguments, and statics, but "this" needs to get flattened. # How to deal with that? kind = symbol_table.kind_of(name) if kind == "local": return Local(name) + elif kind == "static": + return Static(name) else: index = symbol_table.index_of(name) return Location(kind, index, name) @@ -246,9 +251,10 @@ def flatten_statement(stmt: jack_ast.Statement) -> List[Stmt]: expr_stmts, expr = flatten_expression(stmt.expr, force=False) let_stmt = Eval(dest=loc, expr=expr) return expr_stmts + [let_stmt] - elif loc.kind == "static": + elif isinstance(loc, Static): # Note: writing to a static is simple (no need to generate target address), so the # RHS doesn't need to be in a register. + # TODO: so, this isn't really a Store, is it? expr_stmts, expr = flatten_expression(stmt.expr, force=False) let_stmt = Store(loc, expr) return expr_stmts + [let_stmt] @@ -370,7 +376,7 @@ def flatten_expression(expr: jack_ast.Expression, force=True) -> Tuple[List[Stmt elif isinstance(expr, jack_ast.VarRef): loc = resolve_name(expr.name) - if isinstance(loc, Local): + if isinstance(loc, (Local, Static)): return [], loc # Never wrapped elif loc.kind == "this": if isinstance(this_expr, Local): @@ -683,7 +689,7 @@ def rewrite_value(value: Value) -> Tuple[Sequence[Stmt], Value]: return [], value def rewrite_expr(expr: Expr) -> Tuple[List[Stmt], Expr]: - if isinstance(expr, (CallSub, Const, Location)): + if isinstance(expr, (CallSub, Const, Location, Static)): return [], expr elif isinstance(expr, Local): if expr in map: @@ -876,7 +882,7 @@ def rewrite_value(value: Value) -> Value: return value def rewrite_expr(expr: Expr) -> Expr: - if isinstance(expr, (CallSub, Const, Location)): + if isinstance(expr, (CallSub, Const, Location, Static)): return expr elif isinstance(expr, Local): # TODO: a more informative error @@ -1223,8 +1229,7 @@ def handle_Store(self, ast: Store): imm = self.immediate(ast.expr) - kind, index = ast.location.kind, ast.location.idx - if kind == "static": + if isinstance(ast.location, Static): symbol_name = f"{self.class_namespace}.static_{ast.location.name}" if imm is not None: self.asm.instr(f"@{symbol_name}") @@ -1238,10 +1243,8 @@ def handle_Store(self, ast: Store): self.asm.instr(f"@{symbol_name}") self.asm.instr("M=D") - elif kind == "field": - raise Exception(f"should have been rewritten: {ast}") - else: + kind = ast.location.kind if self.leaf_sub_args is not None: # LCL and ARG are not used; just index off of SP (below for args, above for locals) segment_ptr = "SP" @@ -1258,6 +1261,7 @@ def handle_Store(self, ast: Store): segment_ptr = "LCL" else: raise Exception(f"Unknown location: {ast}") + index = ast.location.idx # Super common case: initialize a var to 0 or 1 (or -1): if imm is not None: @@ -1513,11 +1517,7 @@ def handle_Const(self, ast: Const): def handle_Location(self, ast: Location): kind, index = ast.kind, ast.idx - if ast.kind == "static": - symbol_name = f"{self.class_namespace}.static_{ast.name}" - self.asm.instr(f"@{symbol_name}") - self.asm.instr("D=M") - elif ast.kind == "field": + if ast.kind in ("field", "static"): raise Exception(f"should have been rewritten: {ast}") else: if self.leaf_sub_args is not None: @@ -1571,6 +1571,13 @@ def handle_Reg(self, ast: Reg): self.asm.instr(f"@R{5+ast.idx}") self.asm.instr("D=M") + + def handle_Static(self, ast: Static): + symbol_name = f"{self.class_namespace}.static_{ast.name}" + self.asm.instr(f"@{symbol_name}") + self.asm.instr("D=M") + + def handle_Binary(self, ast: Binary): left_imm = self.immediate(ast.left) alu_op = self.binary_op_alu(ast.op) @@ -1597,7 +1604,7 @@ def handle_Binary(self, ast: Binary): # Massive savings here for this compiler; by pushing the comparison all the way into # the ALU, this is one branch, with a specific condition code, to pick the right result. # But having to leave the result in D kind of spoils the party; for one thing, have to - # branch again to skip the no-taken branch, which would have written the other result. + # branch again to skip the not-taken branch, which would have written the other result. # TODO: this is almost certainly wrong for signed values where the difference overflows, though: # -30,000 > 30,000 @@ -1687,6 +1694,8 @@ def describe_expr(self, expr) -> str: return "load" elif isinstance(expr, Reg): return "copy" + elif isinstance(expr, Static): + return "copy" # Confusing? These "loads" are as efficient as reg-reg copies elif isinstance(expr, Binary): return "binary" elif isinstance(expr, Unary): @@ -1699,13 +1708,21 @@ def describe_expr(self, expr) -> str: # Helpers: - def value_to_a(self, ast: Union[Reg, Const]): - """Load a register or constant value into A, without overwriting D, and in one less cycle, - in some cases.""" + def value_to_a(self, ast: Union[Reg, Static, Const]): + """Load a register, static or constant value into A, without overwriting D, and in one less cycle, + in some cases. + + Note: these values are essentially the types in Value, except Locals have been eliminated + at this point. + """ if isinstance(ast, Reg): self.asm.instr(f"@R{5+ast.idx}") self.asm.instr("A=M") + elif isinstance(ast, Static): + symbol_name = f"{self.class_namespace}.static_{ast.name}" + self.asm.instr(f"@{symbol_name}") + self.asm.instr("A=M") elif isinstance(ast, Const): if -1 <= ast.value <= 1: self.asm.instr(f"A={ast.value}") @@ -1854,6 +1871,8 @@ def _Expr_str(expr: Expr) -> str: return f"{expr.name} ({expr.kind} {expr.idx})" elif isinstance(expr, Reg): return f"{expr.name} (r{expr.idx})" + elif isinstance(expr, Static): + return f"{expr.name} (static)" elif isinstance(expr, Binary): return f"{_Expr_str(expr.left)} {expr.op.symbol} {_Expr_str(expr.right)}" elif isinstance(expr, Unary): From 81b544e5c34d18179ad01030258a9c2cb8bbfd3d Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Mon, 19 Feb 2024 13:48:03 -0500 Subject: [PATCH 154/221] Tighter code for Statics appearing in Binary expressions --- alt/reg.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/alt/reg.py b/alt/reg.py index d0ea105..a954cf7 100755 --- a/alt/reg.py +++ b/alt/reg.py @@ -183,7 +183,7 @@ class IndirectRead(NamedTuple): """Get the value given an address, aka peek().""" address: "Value" -Expr = Union[CallSub, Const, Local, Location, Reg, Binary, Unary, IndirectRead] +Expr = Union[CallSub, Const, Local, Location, Reg, Static, Binary, Unary, IndirectRead] class Subroutine(NamedTuple): @@ -1579,18 +1579,18 @@ def handle_Static(self, ast: Static): def handle_Binary(self, ast: Binary): - left_imm = self.immediate(ast.left) + left_symbol = self.symbol(ast.left) alu_op = self.binary_op_alu(ast.op) right_imm = self.immediate(ast.right) - if alu_op == "+" and isinstance(ast.left, Reg) and right_imm is not None: + if alu_op == "+" and left_symbol is not None and right_imm is not None: # e.g. r0 + 1 -> @R5; D=M+1 - self.asm.instr(f"@R{5+ast.left.idx}") + self.asm.instr(f"@{left_symbol}") self.asm.instr(f"D=M{right_imm:+}") return - elif alu_op == "-" and isinstance(ast.left, Reg) and right_imm is not None: + elif alu_op == "-" and left_symbol is not None and right_imm is not None: # e.g. r0 - 1 -> @R5; D=M-1 - self.asm.instr(f"@R{5+ast.left.idx}") + self.asm.instr(f"@{left_symbol}") self.asm.instr(f"D=M{-right_imm:+}") return elif alu_op is not None: @@ -1679,6 +1679,18 @@ def immediate(self, ast: Expr) -> Optional[int]: else: return None + def symbol(self, ast: Expr) -> Optional[str]: + """If the expression is a reference to a Reg or Static which has a known (symbolic) address, + then construct it. + """ + if isinstance(ast, Reg): + return f"R{5+ast.idx}" + elif isinstance(ast, Static): + return f"{self.class_namespace}.static_{ast.name}" + else: + return None + + def describe_expr(self, expr) -> str: """A short suffix categorizing the type of expression, for example 'const'. From b37e87cdce5ad3d4fcf805f08919631075602c50 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Mon, 19 Feb 2024 13:52:33 -0500 Subject: [PATCH 155/221] Reduce interval between checking for tty to keep up with faster reg.py code --- test_12.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test_12.py b/test_12.py index a1eca98..f451734 100755 --- a/test_12.py +++ b/test_12.py @@ -198,8 +198,9 @@ def test_keyboard_lib(keyboard_class=project_12.KEYBOARD_CLASS, platform=BUNDLED def crank(cycles=100_000): """Run for a while, checking the tty for output every few cycles.""" nonlocal output - for _ in range(cycles//100): - computer.ticktock(cycles=100) + CYCLES_PER_CALL = 50 + for _ in range(cycles//50): + computer.ticktock(cycles=CYCLES_PER_CALL) computer.ticktock() c = computer.get_tty() if c != 0: From bb2ad6882ff2d5a1716715465b68f52d72430f65 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Mon, 19 Feb 2024 14:52:17 -0500 Subject: [PATCH 156/221] Regularize handling of Reg and Static (i.e. use Eval) This buys a little more collapsing of Binary/Unary ops --- alt/reg.py | 214 +++++++++++++++++++++++------------------------------ 1 file changed, 91 insertions(+), 123 deletions(-) diff --git a/alt/reg.py b/alt/reg.py index a954cf7..4908540 100755 --- a/alt/reg.py +++ b/alt/reg.py @@ -85,7 +85,7 @@ class Eval(NamedTuple): """Evaluate an expression and store the result somewhere.""" - dest: "Local" # Actually Reg (or Static?) after rewriting + dest: "Local" # Actually Reg or Static after rewriting expr: "Expr" class IndirectWrite(NamedTuple): @@ -94,18 +94,9 @@ class IndirectWrite(NamedTuple): value: "Value" class Store(NamedTuple): - """Store a value to a location identified by segment and index. - - Restrictions on the RHS expr: - - if location is not Static, then expr is always a Value, because the destination address - may need to be computed. - - if location is Static, then expr can be any flattened expression - - And yes, this does imply that storing to a static and storing to argument/local are - actually different operations worthy of their own Stmt types. - """ - location: Union["Location", "Static"] - expr: "Expr" + """Store a value to a location identified by segment and index (i.e. argument or local).""" + location: "Location" + value: "Value" Cmp = str # requires 3.8: Literal["!="] # Meaning "non-zero"; TODO: the rest of the codes @@ -254,9 +245,8 @@ def flatten_statement(stmt: jack_ast.Statement) -> List[Stmt]: elif isinstance(loc, Static): # Note: writing to a static is simple (no need to generate target address), so the # RHS doesn't need to be in a register. - # TODO: so, this isn't really a Store, is it? expr_stmts, expr = flatten_expression(stmt.expr, force=False) - let_stmt = Store(loc, expr) + let_stmt = Eval(loc, expr) return expr_stmts + [let_stmt] elif loc.kind == "this": if isinstance(this_expr, Local): @@ -596,7 +586,7 @@ def analyze_while(stmt: While, live_at_end) -> Tuple[While, Set[str]]: read.update(refs(stmt.address)) read.update(refs(stmt.value)) elif isinstance(stmt, Store): - read.update(refs(stmt.expr)) + read.update(refs(stmt.value)) elif isinstance(stmt, If): # Note: overwriting stmt (and live_set) here: stmt, live_set = analyze_if(stmt, live_set) @@ -725,8 +715,8 @@ def rewrite_statement(stmt: Stmt) -> List[Stmt]: value_stmts, value_value = rewrite_value(stmt.value) return address_stmts + value_stmts + [IndirectWrite(address_value, value_value)] elif isinstance(stmt, Store): - expr_stmts, expr = rewrite_expr(stmt.expr) - return expr_stmts + [Store(stmt.location, expr)] + value_stmts, value = rewrite_expr(stmt.value) + return value_stmts + [Store(stmt.location, value)] elif isinstance(stmt, If): value_stmts, value = rewrite_expr(stmt.value) when_true = rewrite_statements(stmt.when_true) @@ -910,8 +900,8 @@ def rewrite_statement(stmt: Stmt) -> Stmt: value = rewrite_value(stmt.value) return IndirectWrite(address, value) elif isinstance(stmt, Store): - expr = rewrite_expr(stmt.expr) - return Store(stmt.location, expr) + value = rewrite_value(stmt.value) + return Store(stmt.location, value) elif isinstance(stmt, If): value = rewrite_value(stmt.value) when_true = rewrite_statements(stmt.when_true) @@ -1073,7 +1063,7 @@ def uses_stack(stmt: Stmt): elif isinstance(stmt, IndirectWrite): return False elif isinstance(stmt, Store): - return isinstance(stmt.expr, CallSub) + return False elif isinstance(stmt, If): return any(uses_stack(s) for s in stmt.when_true + (stmt.when_false or [])) elif isinstance(stmt, While): @@ -1159,13 +1149,15 @@ def uses_stack(stmt: Stmt): # Statements: def handle_Eval(self, ast: Eval): - assert isinstance(ast.dest, Reg) + assert isinstance(ast.dest, (Reg, Static)) if not isinstance(ast.expr, CallSub): self.asm.start(f"eval-{self.describe_expr(ast.expr)} {_Stmt_str(ast)}") + symbol_name = self.symbol(ast.dest) + # Do the update in-place if possible: - if isinstance(ast.expr, Binary) and isinstance(ast.expr.left, Reg) and ast.dest.idx == ast.expr.left.idx: + if isinstance(ast.expr, Binary) and symbol_name == self.symbol(ast.expr.left): op = self.binary_op_alu(ast.expr.op) if op is not None: right_imm = self.immediate(ast.expr.right) @@ -1173,34 +1165,34 @@ def handle_Eval(self, ast: Eval): if right_imm == 0: pass # nothing to do: rX = rX + 0 else: - self.asm.instr(f"@R{5+ast.dest.idx}") + self.asm.instr(f"@{symbol_name}") self.asm.instr(f"M=M{right_imm:+}") elif op == "-" and right_imm is not None: if right_imm == 0: pass # nothing to do: rX = rX - 0 else: - self.asm.instr(f"@R{5+ast.dest.idx}") + self.asm.instr(f"@{symbol_name}") self.asm.instr(f"M=M{-right_imm:+}") else: self._handle(ast.expr.right) # D = right - self.asm.instr(f"@R{5+ast.dest.idx}") + self.asm.instr(f"@{symbol_name}") self.asm.instr(f"M=M{op}D") return - elif isinstance(ast.expr, Binary) and isinstance(ast.expr.right, Reg) and ast.dest.idx == ast.expr.right.idx: + elif isinstance(ast.expr, Binary) and symbol_name == self.symbol(ast.expr.right): op = self.binary_op_alu(ast.expr.op) if op is not None: self._handle(ast.expr.left) # D = left - self.asm.instr(f"@R{5+ast.dest.idx}") + self.asm.instr(f"@{symbol_name}") self.asm.instr(f"M=D{op}M") return - elif isinstance(ast.expr, Unary) and ast.dest == ast.expr: - self.asm.instr(f"@R{5+ast.dest.idx}") + elif isinstance(ast.expr, Unary) and symbol_name == self.symbol(ast.expr.value): + self.asm.instr(f"@{symbol_name}") self.asm.instr(f"M={self.unary_op(ast.expr.op)}M") return imm = self.immediate(ast.expr) if imm is not None: - self.asm.instr(f"@R{5+ast.dest.idx}") + self.asm.instr(f"@{symbol_name}") self.asm.instr(f"M={imm}") else: self._handle(ast.expr) @@ -1208,7 +1200,7 @@ def handle_Eval(self, ast: Eval): if isinstance(ast.expr, CallSub): self.asm.start(f"eval-result {_Expr_str(ast.dest)} = ") - self.asm.instr(f"@R{5+ast.dest.idx}") + self.asm.instr(f"@{symbol_name}") self.asm.instr("M=D") def handle_IndirectWrite(self, ast: IndirectWrite): @@ -1224,109 +1216,85 @@ def handle_IndirectWrite(self, ast: IndirectWrite): self.asm.instr("M=D") def handle_Store(self, ast: Store): - if not isinstance(ast.expr, CallSub): - self.asm.start(f"store-{self.describe_expr(ast.expr)} {_Stmt_str(ast)}") + self.asm.start(f"store {_Stmt_str(ast)}") - imm = self.immediate(ast.expr) + imm = self.immediate(ast.value) - if isinstance(ast.location, Static): - symbol_name = f"{self.class_namespace}.static_{ast.location.name}" - if imm is not None: - self.asm.instr(f"@{symbol_name}") - self.asm.instr(f"M={imm}") + kind = ast.location.kind + if self.leaf_sub_args is not None: + # LCL and ARG are not used; just index off of SP (below for args, above for locals) + segment_ptr = "SP" + if kind == "argument": + index = -self.leaf_sub_args + ast.location.idx + elif kind == "local": + index = ast.location.idx else: - self._handle(ast.expr) - - if isinstance(ast.expr, CallSub): - self.asm.start(f"store-result {_Expr_str(ast.location)} = ") - - self.asm.instr(f"@{symbol_name}") - self.asm.instr("M=D") - + raise Exception(f"Unknown location: {ast}") else: - kind = ast.location.kind - if self.leaf_sub_args is not None: - # LCL and ARG are not used; just index off of SP (below for args, above for locals) - segment_ptr = "SP" - if kind == "argument": - index = -self.leaf_sub_args + ast.location.idx - elif kind == "local": - index = ast.location.idx - else: - raise Exception(f"Unknown location: {ast}") + if kind == "argument": + segment_ptr = "ARG" + elif kind == "local": + segment_ptr = "LCL" else: - if kind == "argument": - segment_ptr = "ARG" - elif kind == "local": - segment_ptr = "LCL" - else: - raise Exception(f"Unknown location: {ast}") - index = ast.location.idx + raise Exception(f"Unknown location: {ast}") + index = ast.location.idx - # Super common case: initialize a var to 0 or 1 (or -1): - if imm is not None: + # Super common case: initialize a var to 0 or 1 (or -1): + if imm is not None: + if index == 0: + self.asm.instr(f"@{segment_ptr}") + self.asm.instr("A=M") + self.asm.instr(f"M={imm}") + elif index == 1: + self.asm.instr(f"@{segment_ptr}") + self.asm.instr("A=M+1") + self.asm.instr(f"M={imm}") + elif index == -1: + self.asm.instr(f"@{segment_ptr}") + self.asm.instr("A=M-1") + self.asm.instr(f"M={imm}") + elif index > 0: + self.asm.instr(f"@{index}") + self.asm.instr("D=A") + self.asm.instr(f"@{segment_ptr}") + self.asm.instr("A=M+D") + self.asm.instr(f"M={imm}") + else: + self.asm.instr(f"@{-index}") + self.asm.instr("D=A") + self.asm.instr(f"@{segment_ptr}") + self.asm.instr("A=M-D") + self.asm.instr(f"M={imm}") + + else: + # For small index, compute the destination address without clobbering D: + # Note: not optimizing negative indexes because we rarely see store to argument + if 0 <= index <= 6: + self._handle(ast.value) + self.asm.instr(f"@{segment_ptr}") if index == 0: - self.asm.instr(f"@{segment_ptr}") self.asm.instr("A=M") - self.asm.instr(f"M={imm}") - elif index == 1: - self.asm.instr(f"@{segment_ptr}") + else: self.asm.instr("A=M+1") - self.asm.instr(f"M={imm}") - elif index == -1: - self.asm.instr(f"@{segment_ptr}") - self.asm.instr("A=M-1") - self.asm.instr(f"M={imm}") - elif index > 0: + for _ in range(index-1): + self.asm.instr("A=A+1") + self.asm.instr("M=D") + + else: + if index > 0: self.asm.instr(f"@{index}") self.asm.instr("D=A") - self.asm.instr(f"@{segment_ptr}") - self.asm.instr("A=M+D") - self.asm.instr(f"M={imm}") else: self.asm.instr(f"@{-index}") - self.asm.instr("D=A") - self.asm.instr(f"@{segment_ptr}") - self.asm.instr("A=M-D") - self.asm.instr(f"M={imm}") - - else: - # For small index, compute the destination address without clobbering D: - # Note: not optimizing negative indexes because we rarely see store to argument - if 0 <= index <= 6: - self._handle(ast.expr) - - if isinstance(ast.expr, CallSub): - self.asm.start(f"store-result {_Expr_str(ast.location)} = ") - - self.asm.instr(f"@{segment_ptr}") - if index == 0: - self.asm.instr("A=M") - else: - self.asm.instr("A=M+1") - for _ in range(index-1): - self.asm.instr("A=A+1") - self.asm.instr("M=D") - - else: - if index > 0: - self.asm.instr(f"@{index}") - self.asm.instr("D=A") - else: - self.asm.instr(f"@{-index}") - self.asm.instr("D=-A") - self.asm.instr(f"@{segment_ptr}") - self.asm.instr("D=M+D") - self.asm.instr("@R15") # code smell: needing R15 shows that this isn't actually atomic - self.asm.instr("M=D") - self._handle(ast.expr) - - if isinstance(ast.expr, CallSub): - self.asm.start(f"store-result {_Expr_str(ast.location)} = ") - - self.asm.instr("@R15") - self.asm.instr("A=M") - self.asm.instr("M=D") + self.asm.instr("D=-A") + self.asm.instr(f"@{segment_ptr}") + self.asm.instr("D=M+D") + self.asm.instr("@R15") # code smell: needing R15 shows that this isn't actually atomic + self.asm.instr("M=D") + self._handle(ast.value) + self.asm.instr("@R15") + self.asm.instr("A=M") + self.asm.instr("M=D") def handle_If(self, ast: If): self.asm.comment("if...") @@ -1844,7 +1812,7 @@ def _Stmt_str(stmt: Stmt) -> str: elif isinstance(stmt, IndirectWrite): return f"mem[{_Expr_str(stmt.address)}] = {_Expr_str(stmt.value)}" elif isinstance(stmt, Store): - return f"{_Expr_str(stmt.location)} = {_Expr_str(stmt.expr)}" + return f"{_Expr_str(stmt.location)} = {_Expr_str(stmt.value)}" elif isinstance(stmt, If): return "\n".join([ f"if ({_Expr_str(stmt.value)} {stmt.cmp} zero)", From 07845792bc13d9adba1d7fee7572fa255c4f45db Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sat, 24 Feb 2024 12:50:12 -0500 Subject: [PATCH 157/221] Comment on side-effects --- nand/jack_ast.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/nand/jack_ast.py b/nand/jack_ast.py index ff78687..d63f614 100644 --- a/nand/jack_ast.py +++ b/nand/jack_ast.py @@ -33,7 +33,11 @@ class ArrayRef(NamedTuple): array_index: "ExpressionRec" class SubroutineCall(NamedTuple): - """Note: either class_name or var_name or neither may be present.""" + """Note: either class_name or var_name or neither may be present. + Also: this is the only kind of expression that can have side effects. For example, the callee + might update a static or field or write to memory. Therefore order of evaluation needs to + be preserved only when these are involved. + """ class_name: Optional[str] var_name: Optional[str] From b24be0f2ce9d172cf55024c0b553e215a7771741 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sat, 24 Feb 2024 13:08:29 -0500 Subject: [PATCH 158/221] Mangle the interpreter a bit to avoid some argument/local access --- alt/scheme/Interpreter.jack | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/alt/scheme/Interpreter.jack b/alt/scheme/Interpreter.jack index 7f21375..40d0460 100644 --- a/alt/scheme/Interpreter.jack +++ b/alt/scheme/Interpreter.jack @@ -54,7 +54,7 @@ class Interpreter { if (pc[2] = 0) { // jump - let symbol = Interpreter.getTarget(pc[1]); // actually an entry or symbol + let symbol = Interpreter.getTarget(); // actually an entry or symbol let proc = symbol[0]; if (proc[0] & (~31)) { // not between 0 and 31 @@ -88,7 +88,7 @@ class Interpreter { else { // call - let symbol = Interpreter.getTarget(pc[1]); // actually an entry or symbol + let symbol = Interpreter.getTarget(); // actually an entry or symbol let proc = symbol[0]; if (proc[0] & (~31)) { // not between 0 and 31 @@ -120,7 +120,7 @@ class Interpreter { if (opcode = 1) { // set - let symbol = Interpreter.getTarget(pc[1]); // actually an entry or symbol + let symbol = Interpreter.getTarget(); // actually an entry or symbol let symbol[0] = Interpreter.pop(); @@ -130,7 +130,7 @@ class Interpreter { if (opcode = 2) { // get - let symbol = Interpreter.getTarget(pc[1]); // actually an entry or symbol + let symbol = Interpreter.getTarget(); // actually an entry or symbol do Interpreter.push(symbol[0]); @@ -193,18 +193,19 @@ class Interpreter { } /** - Decode the "y" value from a jump/.call, set, or get instruction, and return the rib that + Decode the "y" value from a jump/call, set, or get instruction, and return the rib that contains the target, which might be a stack entry or a symbol. In either case, the actual target is found in the "x" field of the result. */ - function Rib getTarget(int slotOrGlobal) { + function Rib getTarget() { + var int slotOrGlobal; var int i; var Rib ptr; - var Rib symbol; + let slotOrGlobal = pc[1]; if ((slotOrGlobal > -1) & (slotOrGlobal < 1000)) { - let i = slotOrGlobal; // y is slot # of target + let i = slotOrGlobal; let ptr = stack; while (i > 0) { let ptr = ptr[1]; @@ -214,8 +215,7 @@ class Interpreter { } else { // y is addr of target symbol - let symbol = pc[1]; - return symbol; + return slotOrGlobal; } } @@ -239,10 +239,15 @@ class Interpreter { return newStack; } - function void handlePrimitive(int opcode) { + function void handlePrimitive(int opcode_arg) { + var int opcode; var int x, y, z; var Rib tmp; + // Make a local copy, which will end up in a register, avoiding + // 4/5 loads from the stack every time: + let opcode = opcode_arg; + // Note: What you really want here is a computed jump (i.e. a switch construct.) // Failing that, we make do with a binary search, effectively, with 5 branches // to examine the 5 bits of the opcode. From 03b1b163e7a91c8589c79751171a7b90ba64b4d1 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sat, 24 Feb 2024 13:27:18 -0500 Subject: [PATCH 159/221] Manually inline alloc() into push() --- alt/scheme/Interpreter.jack | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/alt/scheme/Interpreter.jack b/alt/scheme/Interpreter.jack index 40d0460..b8820de 100644 --- a/alt/scheme/Interpreter.jack +++ b/alt/scheme/Interpreter.jack @@ -148,8 +148,7 @@ class Interpreter { if (opcode = 4) { // if - let obj = Interpreter.pop(); - if (obj = ribFalse) { + if (Interpreter.pop() = ribFalse) { let pc = pc[2]; } else { @@ -577,7 +576,6 @@ class Interpreter { function Rib alloc(int x, int y, int z) { var Rib r; let r = nextRib; - // let tty[0] = r; // DEBUG let r[0] = x; let r[1] = y; let r[2] = z; @@ -586,7 +584,17 @@ class Interpreter { } function void push(int obj) { - let stack = Interpreter.alloc(obj, stack, 0); // pair-type + // let stack = Interpreter.alloc(obj, stack, 0); // pair-type + + // Avoid function call overhead by doing the allocation directly here: + var Rib r; + let r = nextRib; + let r[0] = obj; + let r[1] = stack; + let r[2] = 0; // pair type + let nextRib = nextRib + 3; + let stack = r; + return; } From 6ec5ca8ec94f085bc776e352290d4de64416816c Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sat, 24 Feb 2024 13:34:34 -0500 Subject: [PATCH 160/221] Split some locals so most end up in registers --- alt/scheme/Interpreter.jack | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/alt/scheme/Interpreter.jack b/alt/scheme/Interpreter.jack index b8820de..cf7ea24 100644 --- a/alt/scheme/Interpreter.jack +++ b/alt/scheme/Interpreter.jack @@ -28,12 +28,17 @@ class Interpreter { starting values. */ function void main() { - var int opcode; - var Rib symbol; - var Rib proc, code, cont, newStack; - var int slot; - var Rib ptr; - var int obj; + // Register-allocated: + var int opcode; // only used to dispatch before any handling + var Rib symbol; // only used to find the value of a symbol + var Rib code; // temp used to extract proc's entry point + var Rib newStack; // temp used when assembling callee's stack + var Rib cont; // existing continuation + + // Stack-allocated: + var Rib proc; // saved to construct cont + var Rib newCont; // saved to update after constructing stack + var Rib symbolToUpdate; // need the location to update after .pop() // Initialize some more state: @@ -63,9 +68,9 @@ class Interpreter { let cont = Interpreter.findContinuation(); // Note: can't overwrite the old continuation because it may be in ROM - let cont = Interpreter.alloc(cont[0], proc, cont[2]); + let newCont = Interpreter.alloc(cont[0], proc, cont[2]); - let stack = Interpreter.wrangleClosureParams(proc, cont); + let stack = Interpreter.wrangleClosureParams(proc, newCont); let code = proc[0]; let pc = code[2]; @@ -98,9 +103,9 @@ class Interpreter { // x = saved stack (after popping args) // y = proc rib // z = next instruction - let cont = Interpreter.alloc(-1, proc, pc[2]); - let newStack = Interpreter.wrangleClosureParams(proc, cont); - let cont[0] = stack; + let newCont = Interpreter.alloc(-1, proc, pc[2]); + let newStack = Interpreter.wrangleClosureParams(proc, newCont); + let newCont[0] = stack; let stack = newStack; // Now jump to the entry point of the proc: @@ -120,9 +125,9 @@ class Interpreter { if (opcode = 1) { // set - let symbol = Interpreter.getTarget(); // actually an entry or symbol + let symbolToUpdate = Interpreter.getTarget(); // actually an entry or symbol - let symbol[0] = Interpreter.pop(); + let symbolToUpdate[0] = Interpreter.pop(); let pc = pc[2]; } From 3520072eaaed44c190b4400848cbec4a7de6fbf0 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sat, 24 Feb 2024 13:43:18 -0500 Subject: [PATCH 161/221] Split proc used only for dispatch from saved copy --- alt/scheme/Interpreter.jack | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/alt/scheme/Interpreter.jack b/alt/scheme/Interpreter.jack index cf7ea24..c643339 100644 --- a/alt/scheme/Interpreter.jack +++ b/alt/scheme/Interpreter.jack @@ -34,10 +34,11 @@ class Interpreter { var Rib code; // temp used to extract proc's entry point var Rib newStack; // temp used when assembling callee's stack var Rib cont; // existing continuation + var Rib proc; // used only for dispatch // Stack-allocated: - var Rib proc; // saved to construct cont - var Rib newCont; // saved to update after constructing stack + var Rib savedProc; // saved to construct cont + var Rib newCont; // saved to update after constructing stack var Rib symbolToUpdate; // need the location to update after .pop() // Initialize some more state: @@ -65,14 +66,16 @@ class Interpreter { if (proc[0] & (~31)) { // not between 0 and 31 // closure + let savedProc = proc; + let cont = Interpreter.findContinuation(); // Note: can't overwrite the old continuation because it may be in ROM - let newCont = Interpreter.alloc(cont[0], proc, cont[2]); + let newCont = Interpreter.alloc(cont[0], savedProc, cont[2]); - let stack = Interpreter.wrangleClosureParams(proc, newCont); + let stack = Interpreter.wrangleClosureParams(savedProc, newCont); - let code = proc[0]; + let code = savedProc[0]; let pc = code[2]; } else { @@ -99,17 +102,19 @@ class Interpreter { if (proc[0] & (~31)) { // not between 0 and 31 // closure + let savedProc = proc; + // New continuation: // x = saved stack (after popping args) // y = proc rib // z = next instruction - let newCont = Interpreter.alloc(-1, proc, pc[2]); - let newStack = Interpreter.wrangleClosureParams(proc, newCont); + let newCont = Interpreter.alloc(-1, proc, pc[2]); // tricky: faster to use proc from register in first call + let newStack = Interpreter.wrangleClosureParams(savedProc, newCont); let newCont[0] = stack; let stack = newStack; // Now jump to the entry point of the proc: - let code = proc[0]; + let code = savedProc[0]; let pc = code[2]; } else { From 1917d0c9baf1c78441874172020c34c332e94dc3 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sat, 24 Feb 2024 13:58:04 -0500 Subject: [PATCH 162/221] Commit some commented code related to inlining --- alt/scheme/Interpreter.jack | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/alt/scheme/Interpreter.jack b/alt/scheme/Interpreter.jack index c643339..f4c725e 100644 --- a/alt/scheme/Interpreter.jack +++ b/alt/scheme/Interpreter.jack @@ -609,6 +609,7 @@ class Interpreter { } /** Discard the top entry from the stack, return its CAR. */ + // TODO: get the compiler to inline this function int pop() { var int r; let r = stack[0]; @@ -616,6 +617,19 @@ class Interpreter { return r; } + // /** Get the object on the top of the stack, without removing it. */ + // // TODO: get the compiler to inline this + // function int peek() { + // return stack[0]; + // } + + // /** Overwrite the object on the top of the stack, avoiding allocation. */ + // // TODO: get the compiler to inline this + // function void replace(int obj) { + // let stack[0] = obj; + // return; + // } + /** Address of the continuation rib in the current stack frame: the first entry in the stack with z != 0. From cbd38e3a17006883f6929925b9b19285d4160f43 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sat, 24 Feb 2024 15:51:51 -0500 Subject: [PATCH 163/221] Consistently simlify comparison expressions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Same simplification applied when branching as when combining values. Collapse a few more patterns, e.g. “x < 1”, to direct comparison with 0. This helps mostly because Jack makes it awkward to say simple things like “x >= 0” --- alt/reg.py | 103 ++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 82 insertions(+), 21 deletions(-) diff --git a/alt/reg.py b/alt/reg.py index 4908540..8518bac 100755 --- a/alt/reg.py +++ b/alt/reg.py @@ -309,23 +309,14 @@ def flatten_condition(expr: jack_ast.Expression) -> Tuple[List[Stmt], Expr, Cmp] preparing a value to be compared with zero. """ - # Collapse simple negated conditions: - if isinstance(expr, jack_ast.UnaryExpression) and expr.op.symbol == "~": - if isinstance(expr.expr, jack_ast.BinaryExpression) and expr.expr.op.symbol == "<": - expr = jack_ast.BinaryExpression(expr.expr.left, jack_ast.Op(">="), expr.expr.right) - elif isinstance(expr.expr, jack_ast.BinaryExpression) and expr.expr.op.symbol == ">": - expr = jack_ast.BinaryExpression(expr.expr.left, jack_ast.Op("<="), expr.expr.right) - elif isinstance(expr.expr, jack_ast.BinaryExpression) and expr.expr.op.symbol == "=": - expr = jack_ast.BinaryExpression(expr.expr.left, jack_ast.Op("!="), expr.expr.right) + # First apply AST-level simplification, which reduces the number of possible shapes: + expr = simplify_expression(expr) # Collapse anything that's become a comparison between two values: if isinstance(expr, jack_ast.BinaryExpression) and expr.op.symbol in ("<", ">", "=", "<=", ">=", "!="): if expr.right == jack_ast.IntegerConstant(0): left_stmts, left_value = flatten_expression(expr.left) return left_stmts, left_value, expr.op.symbol - elif expr.left == jack_ast.IntegerConstant(0): - right_stmts, right_value = flatten_expression(expr.right) - return right_stmts, right_value, negate_cmp(expr.op.symbol) else: left_stmts, left_value = flatten_expression(expr.left) right_stmts, right_value = flatten_expression(expr.right) @@ -336,8 +327,13 @@ def flatten_condition(expr: jack_ast.Expression) -> Tuple[List[Stmt], Expr, Cmp] expr_stmts, expr_value = flatten_expression(expr) return expr_stmts, expr_value, "!=" + def negate_cmp(cmp: Cmp) -> Cmp: + """Negate the value.""" return {"<": ">=", ">": "<=", "=": "!=", "<=": ">", ">=": "<", "!=": "="}[cmp] + def invert_cmp(cmp: Cmp) -> Cmp: + """Reverse the operand order.""" + return {"<": ">", ">": "<", "=": "=", "<=": ">=", ">=": "<=", "!=": "!="}[cmp] def flatten_expression(expr: jack_ast.Expression, force=True) -> Tuple[List[Stmt], Expr]: """Reduce an expression to something that's definitely trivial, possibly preceded @@ -348,6 +344,8 @@ def flatten_expression(expr: jack_ast.Expression, force=True) -> Tuple[List[Stmt of Eval or Push. """ + expr = simplify_expression(expr) + if isinstance(expr, jack_ast.IntegerConstant): return [], Const(expr.value) @@ -431,16 +429,10 @@ def flatten_expression(expr: jack_ast.Expression, force=True) -> Tuple[List[Stmt flat_expr = CallSub(target_class, expr.sub_name, len(expr.args) + 1) elif isinstance(expr, jack_ast.BinaryExpression): - # TODO: this transformation is the same as the standard compiler; share that code? - if expr.op.symbol == "*": - return flatten_expression(jack_ast.SubroutineCall("Math", None, "multiply", [expr.left, expr.right]), force=force) - elif expr.op.symbol == "/": - return flatten_expression(jack_ast.SubroutineCall("Math", None, "divide", [expr.left, expr.right]), force=force) - else: - left_stmts, left_expr = flatten_expression(expr.left) - right_stmts, right_expr = flatten_expression(expr.right) - stmts = left_stmts + right_stmts - flat_expr = Binary(left_expr, expr.op, right_expr) + left_stmts, left_expr = flatten_expression(expr.left) + right_stmts, right_expr = flatten_expression(expr.right) + stmts = left_stmts + right_stmts + flat_expr = Binary(left_expr, expr.op, right_expr) elif isinstance(expr, jack_ast.UnaryExpression): stmts, child_expr = flatten_expression(expr.expr) @@ -484,6 +476,72 @@ def flatten_expression(expr: jack_ast.Expression, force=True) -> Tuple[List[Stmt return Subroutine(ast.name, num_args, num_vars, statements) +def simplify_expression(expr: jack_ast.Expression) -> jack_ast.BinaryExpression: + """Reduce/canonicalize conditions to a more regular form: + + Replace * and / expressions with calls to multiply/divide. + + For comparison expressions: + - if there's a constant, it's on the right + - if the constant can be re-written to 0, it is + - flatten negated conditions and negative integer constants + """ + + if isinstance(expr, jack_ast.UnaryExpression) and expr.op.symbol == "~": + if isinstance(expr.expr, jack_ast.BinaryExpression) and expr.expr.op.symbol == "<": + expr = jack_ast.BinaryExpression(expr.expr.left, jack_ast.Op(">="), expr.expr.right) + elif isinstance(expr.expr, jack_ast.BinaryExpression) and expr.expr.op.symbol == ">": + expr = jack_ast.BinaryExpression(expr.expr.left, jack_ast.Op("<="), expr.expr.right) + elif isinstance(expr.expr, jack_ast.BinaryExpression) and expr.expr.op.symbol == "=": + expr = jack_ast.BinaryExpression(expr.expr.left, jack_ast.Op("!="), expr.expr.right) + + def simplify_constant(x: jack_ast.Expression) -> Optional[int]: + """If possible, reduce the expression to a (possibly negative) constant.""" + if isinstance(x, jack_ast.UnaryExpression) and x.op.symbol == "-" and isinstance(x.expr, jack_ast.IntegerConstant): + return -x.expr.value + elif isinstance(x, jack_ast.IntegerConstant): + return x.value + else: + return None + + if isinstance(expr, jack_ast.BinaryExpression): + # TODO: this transformation is the same as the standard compiler; share that code? + if expr.op.symbol == "*": + return jack_ast.SubroutineCall("Math", None, "multiply", [expr.left, expr.right]) + elif expr.op.symbol == "/": + return jack_ast.SubroutineCall("Math", None, "divide", [expr.left, expr.right]) + elif expr.op.symbol in ("<", ">", "=", "<=", ">=", "!="): + def invert_cmp(cmp: Cmp) -> Cmp: + """Reverse the operand order.""" + return {"<": ">", ">": "<", "=": "=", "<=": ">=", ">=": "<=", "!=": "!="}[cmp] + + simple_left = simplify_constant(expr.left) + simple_right = simplify_constant(expr.right) + + if simple_left is not None and simple_right is None: + # Constant on the left; reverse the order: + return simplify_expression(jack_ast.BinaryExpression(expr.right, + jack_ast.Op(invert_cmp(expr.op.symbol)), + expr.left)) + elif simple_right is not None: + # Constant on the right; match some common cases: + zero = jack_ast.IntegerConstant(0) + if expr.op.symbol == "<" and simple_right == 1: + return jack_ast.BinaryExpression(expr.left, jack_ast.Op("<="), zero) + elif expr.op.symbol == ">=" and simple_right == 1: + return jack_ast.BinaryExpression(expr.left, jack_ast.Op(">"), zero) + elif expr.op.symbol == ">" and simple_right == -1: + return jack_ast.BinaryExpression(expr.left, jack_ast.Op(">="), zero) + elif expr.op.symbol == "<=" and simple_right == -1: + return jack_ast.BinaryExpression(expr.left, jack_ast.Op("<"), zero) + else: + return jack_ast.BinaryExpression(expr.left, + expr.op, + jack_ast.IntegerConstant(simple_right)) + + return expr + + class LiveStmt(NamedTuple): statement: Stmt before: Set[str] @@ -1620,6 +1678,9 @@ def binary_op_cmp(self, op: jack_ast.Op) -> Optional[str]: "<": "LT", ">": "GT", "=": "EQ", + "<=": "LE", + ">=": "GE", + "!=": "NE", }.get(op.symbol) def handle_Unary(self, ast: Unary): From 4fbc176f0c17f5feddb2cdf321d37cd6dd5c8931 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sat, 24 Feb 2024 20:59:38 -0500 Subject: [PATCH 164/221] Remove a few instructions from the return sequence --- alt/reg.py | 43 +++++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/alt/reg.py b/alt/reg.py index 8518bac..0a6e952 100755 --- a/alt/reg.py +++ b/alt/reg.py @@ -113,8 +113,8 @@ class While(NamedTuple): body: Sequence["Stmt"] class Return(NamedTuple): - """Evaluate expr, store the value in the "RESULT" register, and return to the caller.""" - expr: "Expr" + """Evaluate expr, store the value (if present) in the "RESULT" register, and return to the caller.""" + expr: Optional["Expr"] class Push(NamedTuple): """Used only with Subroutine calls. Acts like Eval, but the destination is the stack.""" @@ -299,7 +299,7 @@ def flatten_statement(stmt: jack_ast.Statement) -> List[Stmt]: stmts, expr = flatten_expression(stmt.expr, force=False) return stmts + [Return(expr)] else: - return [Return(Const(0))] + return [Return(None)] else: raise Exception(f"Unknown statement: {stmt}") @@ -786,8 +786,11 @@ def rewrite_statement(stmt: Stmt) -> List[Stmt]: body = rewrite_statements(stmt.body) return [While(test + value_stmts, value, stmt.cmp, body)] elif isinstance(stmt, Return): - value_stmts, value = rewrite_expr(stmt.expr) - return value_stmts + [Return(value)] + if stmt.expr is not None: + value_stmts, value = rewrite_expr(stmt.expr) + return value_stmts + [Return(value)] + else: + return [Return(None)] elif isinstance(stmt, Push): expr_stmts, expr = rewrite_expr(stmt.expr) return expr_stmts + [Push(expr)] @@ -971,7 +974,7 @@ def rewrite_statement(stmt: Stmt) -> Stmt: body = rewrite_statements(stmt.body) return While(test, value, stmt.cmp, body) elif isinstance(stmt, Return): - value = rewrite_expr(stmt.expr) + value = rewrite_expr(stmt.expr) if stmt.expr is not None else None return Return(value) elif isinstance(stmt, Push): expr = rewrite_expr(stmt.expr) @@ -1425,7 +1428,7 @@ def handle_Return(self, ast: Return): self.handle_CallSub(ast.expr) self.asm.comment(f"leave the result in {RESULT}") - else: + elif ast.expr is not None: self.asm.start(f"eval-{self.describe_expr(ast.expr)} {_Expr_str(ast.expr)} (for return)") # Save a cycle for "return 0": @@ -1441,12 +1444,21 @@ def handle_Return(self, ast: Return): if self.leaf_sub_args is not None: self.asm.start("return (from leaf)") if self.leaf_sub_args > 0: - # TODO: special-case 1 and 2 arguments to save 2/1 instructions - self.asm.comment("pop arguments") - self.asm.instr(f"@{self.leaf_sub_args}") - self.asm.instr("D=A") - self.asm.instr("@SP") - self.asm.instr("M=M-D") + if self.leaf_sub_args == 1: + self.asm.comment("pop 1 argument") + self.asm.instr("@SP") + self.asm.instr("M=M-1") + elif self.leaf_sub_args == 2: + self.asm.comment("pop 2 arguments") + self.asm.instr("@SP") + self.asm.instr("M=M-1") + self.asm.instr("M=M-1") + else: + self.asm.comment("pop arguments") + self.asm.instr(f"@{self.leaf_sub_args}") + self.asm.instr("D=A") + self.asm.instr("@SP") + self.asm.instr("M=M-D") self.asm.instr(f"@{RETURN_ADDRESS}") self.asm.instr("A=M") self.asm.instr("0;JMP") @@ -1886,7 +1898,10 @@ def _Stmt_str(stmt: Stmt) -> str: elif isinstance(stmt, While): return f"while ({'; '.join(_Stmt_str(s) for s in stmt.test)}; {_Expr_str(stmt.value)} {stmt.cmp} zero)\n" + jack_ast._indent("\n".join(_Stmt_str(s) for s in stmt.body)) elif isinstance(stmt, Return): - return f"return {_Expr_str(stmt.expr)}" + if stmt.expr is not None: + return f"return {_Expr_str(stmt.expr)}" + else: + return "return" elif isinstance(stmt, Push): return f"push {_Expr_str(stmt.expr)}" elif isinstance(stmt, Discard): From 298f5de679fed66e71cf53efd7ff8762dc6b69a4 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 25 Feb 2024 18:39:18 -0500 Subject: [PATCH 165/221] =?UTF-8?q?Reduce=20memory=20traffic=20for=20?= =?UTF-8?q?=E2=80=9Cregisters=E2=80=9D=20by=20assigning=20some=20temps=20t?= =?UTF-8?q?o=20D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only if/while test values for now (and it looks like there might be a subtle bug with while.) --- alt/README.md | 2 +- alt/reg.py | 212 +++++++++++++++++++++++++++++++++++++++--------- alt/test_reg.py | 36 ++++++++ 3 files changed, 212 insertions(+), 38 deletions(-) diff --git a/alt/README.md b/alt/README.md index 999190f..8b530c6 100644 --- a/alt/README.md +++ b/alt/README.md @@ -54,7 +54,7 @@ replaces certain function calls with lower-overhead "reduced" alternatives. | [alt/eight.py](eight.py) | 1,032 (-18%) | _same_ | +100% | +100% | | [alt/big.py](big.py) | 1,448 (+14%) | ? | ? | ? | | [alt/lazy.py](lazy.py) | _same_ | 23,650 (-8%) | 37,300 (-10%) | 111,000 (-14%) | -| [alt/reg.py](reg.py) | _same_ | 20,300 (-21%) | 15,700 (-62%) | 58,800 (-54%) | +| [alt/reg.py](reg.py) | _same_ | 19,800 (-23%) | 14,700 (-64%) | 57,700 (-55%) | | [alt/reduce.py](reduce.py) | _same_ | 27,350 (+6.5%) | 20,300 (-51%) | _same_ | diff --git a/alt/reg.py b/alt/reg.py index 0a6e952..88a7e09 100755 --- a/alt/reg.py +++ b/alt/reg.py @@ -41,6 +41,7 @@ correct arguments, locals, return addresses, and result values. """ +from collections import Counter import itertools from os import name from typing import Dict, Generic, List, NamedTuple, Optional, Sequence, Set, Tuple, TypeVar, Union @@ -60,6 +61,18 @@ Possibly makes tracing/debugging confusing or useless in these functions. """ +PRINT_LIVENESS = True +"""Print each function before assigning variables to registers. +Each statement is anootated with the set of variables which are 'live' at the beginning of +that statement. A live variable contains a value that will be used later; it must nt be overwritten +at that point.""" + +PRINT_IR = True +"""Print each function immediately before emitting code. +Variables have been assigned to concrete locations, and each statement represents a unit +of work for which code can be generated directly. +""" + # IR # @@ -113,7 +126,7 @@ class While(NamedTuple): body: Sequence["Stmt"] class Return(NamedTuple): - """Evaluate expr, store the value (if present) in the "RESULT" register, and return to the caller.""" + """Evaluate expr (if present), store the value in the "RESULT" register, and return to the caller.""" expr: Optional["Expr"] class Push(NamedTuple): @@ -158,6 +171,12 @@ class Static(NamedTuple): """A static variable; just as efficient to access as Reg.""" name: str +class Temp(NamedTuple): + """Pseudo-register location used when we know a variable is live only between adjacent + instructions, and it's safe to simply store it in D.""" + name: str # for debugging + + Value = Union[Const, Local, Reg, Static] """A value that's eligible to be referenced in any statement or expression.""" @@ -920,15 +939,16 @@ def visit_stmts(stmts: Optional[Sequence[LiveStmt]]): return color_sets -def lock_down_locals(stmts: Sequence[Stmt], map: Dict[Local, Reg]) -> List[Stmt]: +def resolve_locals(stmts: Sequence[Stmt], map: Dict[Local, V], fail_on_unmapped=True) -> List[Stmt]: """Rewrite statements and expressions, updating references to locals to refer to the given - registers. If any local is not accounted for, fail. + registers. If fail_on_unmapped and any local is not accounted for, fail. """ def rewrite_value(value: Value) -> Value: - if isinstance(value, Local): - # TODO: a more informative error + if value in map: return map[value] + elif fail_on_unmapped and isinstance(value, Local): + raise Exception(f"No assignment for local: {value}") else: return value @@ -936,8 +956,7 @@ def rewrite_expr(expr: Expr) -> Expr: if isinstance(expr, (CallSub, Const, Location, Static)): return expr elif isinstance(expr, Local): - # TODO: a more informative error - return map[expr] + return rewrite_value(expr) elif isinstance(expr, Binary): left_value = rewrite_value(expr.left) right_value = rewrite_value(expr.right) @@ -993,6 +1012,89 @@ def rewrite_statements(stmts: Optional[Sequence[Stmt]]) -> Optional[List[Stmt]]: return rewrite_statements(stmts) +def find_transients(stmts: Sequence[LiveStmt]) -> Set[str]: + """Identify Locals that exist only to carry a result between a pair of adjacent statements, + where the code generator is able to keep the value in D: + - live only in one statement (i.e. assigned in one, then used in the next) + - the statement that uses it doesn't need to overwrite D + """ + + counts = Counter() + + def count_stmt(s: LiveStmt): + for l in s.before: counts[l] += 1 + if isinstance(s.statement, If): + for cs in s.statement.when_true: count_stmt(cs) + for cs in s.statement.when_false or []: count_stmt(cs) + elif isinstance(s.statement, While): + for cs in s.statement.test: count_stmt(cs) + for cs in s.statement.body: count_stmt(cs) + # Tricky: the value's refs aren't represented in LiveStmt + for l in refs(s.statement.value): + counts[l] += 1 + + for s in stmts: count_stmt(s) + + # Candidate locals: live only in a single statement, the one where they're used + one_time = { l for (l, x) in counts.items() if x == 1 } + + # Try re-writing the statement where the var is used: + def visit_stmts(l: Local, stmts: Sequence[LiveStmt]) -> bool: + return any (visit_stmt(l, s) for s in stmts) + + def visit_stmt(l: Local, stmt: LiveStmt) -> bool: + if isinstance(stmt.statement, If): + if any(is_translatable(s) for s in stmt.statement.when_true): return True + elif (stmt.statement.when_false is not None + and any(is_translatable(s) for s in stmt.statement.when_false)): return True + elif isinstance(stmt.statement, While): + if l in refs(stmt.statement.value): return True + elif any(is_translatable(s) for s in stmt.statement.test): return True + elif any(is_translatable(s) for s in stmt.statement.body): return True + + if l in stmt.before: + # TODO: rewrite the expression + return is_translatable(stmt.statement) + # if isinstance(stmt.statement, Eval): + # # TODO: rewrite the expression + # return is_translatable(stmt.statement) + # elif isinstance(stmt.statement, IndirectWrite): + # # TODO: rewrite the expression + # return is_translatable(stmt.statement) + # elif isinstance(stmt.statement, Store): + # # TODO: rewrite the expression + # return is_translatable(stmt.statement) + # elif isinstance(stmt.statement, Return): + # # TODO: rewrite the expression + # return is_translatable(stmt.statement) + # elif isinstance(stmt.statement, Push): + # # TODO: rewrite the expression + # return is_translatable(stmt.statement) + # elif isinstance(stmt.statement, Discard): + # # TODO: rewrite the expression + # return is_translatable(stmt.statement) + else: + return False + + # FIXME: this is patently O(n^2). Fix that by constructing a map of one-time locals to the + # statements that use them, and then only inspect those. + return { l for l in one_time if visit_stmts(l, stmts) } + + +def is_translatable(stmt: Stmt): + """True if the statement can be translated to assembly without disturbing Temp values stored in D. + The statement may contain unresolved Locals; they are assumed to represent Reg locations that + will be assigned later. + """ + + if isinstance(stmt, If): # and isinstance(stmt.value, Temp): + # Actually, probably the value is only ever a Temp if we're asking the question + return True + # TODO: what else? + else: + return False + + def phase_two(ast: Subroutine, reg_count: int = 8) -> Subroutine: """The input is IR with all locals represented by Local. @@ -1003,6 +1105,11 @@ def phase_two(ast: Subroutine, reg_count: int = 8) -> Subroutine: # TODO: treat THIS and THAT as additional locations for "saved" vars. # TODO: color vars needing saving as well? probably not worth it for stack-allocated. + # + # Identify locals that need to be stored on the stack (because their values + # need to persist across subroutine calls): + # + promoted_count = 0 def next_location(var: Local) -> Location: nonlocal promoted_count @@ -1014,45 +1121,63 @@ def next_location(var: Local) -> Location: need_promotion = need_saving(liveness) - # for s in liveness: - # print(_Stmt_str(s)) - # print(f"need saving: {need_promotion}") + if PRINT_LIVENESS: + print(ast._replace(body=liveness)) + print(f"need saving: [{', '.join(str(n) for n in need_promotion)}]") + print() body = promote_locals(ast.body, { Local(l): next_location(Local(l)) for l in need_promotion }, "p_") - while True: - liveness2 = analyze_liveness(body) - need_promotion2 = need_saving(liveness2) + liveness2 = analyze_liveness(body) + need_promotion2 = need_saving(liveness2) - # Sanity check: additional promotion - if len(need_promotion2) > 0: - # for s in liveness2: - # print(_Stmt_str(s)) - # print(f"need saving after one round: {need_promotion2}") - raise Exception(f"More than one round of promotion needed. Need promotion: {need_promotion2}; in {ast.name}()") + if PRINT_LIVENESS: + print(ast._replace(body=liveness2)) - local_sets = color_locals(liveness2) + # Sanity check: additional promotion needed + if len(need_promotion2) > 0: + raise Exception(f"One-pass promotion inadequate Need promotion: {need_promotion2}; in {ast.name}()") - if len(local_sets) > reg_count: - unallocatable_sets = local_sets[reg_count:] - print(f"Unable to fit local variables in {reg_count} registers; no space for {[ {l.name for l in s} for s in unallocatable_sets]}") + # + # Identify temps that can be passed in D for zero overhead: + # - body = promote_locals(body, { l: next_location(l) for s in unallocatable_sets for l in s }, "q_") + transients = find_transients(liveness2) + body = resolve_locals(body, {Local(l): Temp(l) for l in transients}, fail_on_unmapped=False) + liveness3 = analyze_liveness(body) # TODO: is it safe to re-analyze? - else: - reg_map = { - l: Reg(idx=i, name=l.name) - for i, ls in enumerate(local_sets) - for l in ls - } + if PRINT_LIVENESS: + print(ast._replace(body=liveness3)) + print() + + # + # Anything left now can be stored in a register, and they better all fit: + # + + local_sets = color_locals(liveness3) - reg_pressure = 0 if reg_map == {} else max(r.idx+1 for r in reg_map.values()) - # print(f"Registers allocated in {ast.name}: {reg_pressure} ({100*reg_pressure/reg_count:.1f}%)") + if len(local_sets) > reg_count: + unallocatable_sets = local_sets[reg_count:] - reg_body = lock_down_locals(body, reg_map) + print(f"Unable to fit local variables in {reg_count} registers; no space for {[ {l.name for l in s} for s in unallocatable_sets]}") - return Subroutine(ast.name, ast.num_args, promoted_count, reg_body) + raise Exception("One-pass promotion failed!?") + # body = promote_locals(body, { l: next_location(l) for s in unallocatable_sets for l in s }, "q_") + + else: + reg_map = { + l: Reg(idx=i, name=l.name) + for i, ls in enumerate(local_sets) + for l in ls + } + + reg_pressure = 0 if reg_map == {} else max(r.idx+1 for r in reg_map.values()) + # print(f"Registers allocated in {ast.name}: {reg_pressure} ({100*reg_pressure/reg_count:.1f}%)") + + reg_body = resolve_locals(body, reg_map, fail_on_unmapped=True) + + return Subroutine(ast.name, ast.num_args, promoted_count, reg_body) def compile_class(ast: jack_ast.Class) -> Class: @@ -1101,7 +1226,8 @@ def translate_class(self, class_ast: Class): self.translate_subroutine(s, class_ast.name) def translate_subroutine(self, subroutine_ast: Subroutine, class_name: str): - # print(subroutine_ast) + if PRINT_IR: + print(subroutine_ast); print() # if self.last_function_start is not None: # instrs = self.asm.instruction_count - self.last_function_start @@ -1210,11 +1336,16 @@ def uses_stack(stmt: Stmt): # Statements: def handle_Eval(self, ast: Eval): - assert isinstance(ast.dest, (Reg, Static)) + assert isinstance(ast.dest, (Reg, Static, Temp)) if not isinstance(ast.expr, CallSub): self.asm.start(f"eval-{self.describe_expr(ast.expr)} {_Stmt_str(ast)}") + # Easy case: leave the result in D for the next statement to pick up + if isinstance(ast.dest, Temp): + self._handle(ast.expr) + return + symbol_name = self.symbol(ast.dest) # Do the update in-place if possible: @@ -1441,8 +1572,8 @@ def handle_Return(self, ast: Return): self.asm.instr(f"@{RESULT}") self.asm.instr("M=D") + self.asm.start("return (from leaf)") if self.leaf_sub_args is not None: - self.asm.start("return (from leaf)") if self.leaf_sub_args > 0: if self.leaf_sub_args == 1: self.asm.comment("pop 1 argument") @@ -1615,6 +1746,11 @@ def handle_Static(self, ast: Static): self.asm.instr(f"@{symbol_name}") self.asm.instr("D=M") + def handle_Temp(self, ast: Temp): + """Yesssss. The contract is to load the value into D, and Temp means the value is + already in D when we get here. + """ + pass def handle_Binary(self, ast: Binary): left_symbol = self.symbol(ast.left) @@ -1929,6 +2065,8 @@ def _Expr_str(expr: Expr) -> str: return f"{expr.name} (r{expr.idx})" elif isinstance(expr, Static): return f"{expr.name} (static)" + elif isinstance(expr, Temp): + return f"{expr.name} (D)" elif isinstance(expr, Binary): return f"{_Expr_str(expr.left)} {expr.op.symbol} {_Expr_str(expr.right)}" elif isinstance(expr, Unary): diff --git a/alt/test_reg.py b/alt/test_reg.py index 93abc0e..abe7495 100755 --- a/alt/test_reg.py +++ b/alt/test_reg.py @@ -10,6 +10,42 @@ from alt.reg import _Stmt_str +def test_simplify_expression(): + binary = jack_ast.BinaryExpression + def tilde(exp): return jack_ast.UnaryExpression(jack_ast.Op("~"), exp) + x = jack_ast.VarRef("x") + op = jack_ast.Op + const = jack_ast.IntegerConstant + def neg(exp): return jack_ast.UnaryExpression(jack_ast.Op("-"), exp) + + assert (simplify_expression(binary(x, op("<"), const(0))) + == binary(x, op("<"), const(0))) + + assert (simplify_expression(tilde(binary(x, op("<"), const(0)))) + == binary(x, op(">="), const(0))) + + assert (simplify_expression(binary(x, op(">"), neg(const(100)))) + == binary(x, op(">"), const(-100))) + + assert (simplify_expression(binary(const(0), op("<"), x)) + == binary(x, op(">"), const(0))) + + # These case might seem fiddly, but they do tend to come up and comparing with 0 + # is just a lot simpler: + assert (simplify_expression(binary(x, op(">"), const(-1))) + == binary(x, op(">="), const(0))) + + assert (simplify_expression(binary(x, op("<="), const(-1))) + == binary(x, op("<"), const(0))) + + assert (simplify_expression(binary(x, op("<"), const(1))) + == binary(x, op("<="), const(0))) + + assert (simplify_expression(binary(x, op(">="), const(1))) + == binary(x, op(">"), const(0))) + + + def test_if_liveness(): src = """ class Main { From 34748c45642f13b8e4a0552f13d24cc11d130eca Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Mon, 26 Feb 2024 17:07:29 -0500 Subject: [PATCH 166/221] Fix while liveness analysis MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When var is inlined into the tested value directly, it’s was being ignored. Previously that value always ended up in a temp, and that assignment was properly tracked, so the error was undetectable. --- alt/reg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alt/reg.py b/alt/reg.py index 88a7e09..65e9cea 100755 --- a/alt/reg.py +++ b/alt/reg.py @@ -638,7 +638,7 @@ def analyze_while(stmt: While, live_at_end) -> Tuple[While, Set[str]]: if len(test_liveness) > 0: live_at_test_start = test_liveness[0].before else: - live_at_test_start = live_at_body_start + live_at_test_start = live_at_test_end stmt = While(test_liveness, stmt.value, stmt.cmp, body_liveness) live = live_at_test_start From 00632748cd9ab52dd6acbcf14ef4e0c120f1e21e Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Mon, 26 Feb 2024 17:09:16 -0500 Subject: [PATCH 167/221] Inline negated values in conditions --- alt/reg.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/alt/reg.py b/alt/reg.py index 65e9cea..513bb7d 100755 --- a/alt/reg.py +++ b/alt/reg.py @@ -342,6 +342,9 @@ def flatten_condition(expr: jack_ast.Expression) -> Tuple[List[Stmt], Expr, Cmp] diff_var = next_var() diff_stmt = Eval(diff_var, Binary(left_value, jack_ast.Op("-"), right_value)) return left_stmts + right_stmts + [diff_stmt], diff_var, expr.op.symbol + elif isinstance(expr, jack_ast.UnaryExpression) and expr.op.symbol == "~": + expr_stmts, expr_value = flatten_expression(expr.expr) + return expr_stmts, expr_value, "=" else: expr_stmts, expr_value = flatten_expression(expr) return expr_stmts, expr_value, "!=" From b0b1ebfb836ee8324ca797ba777830ed3887abe1 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Mon, 26 Feb 2024 18:55:56 -0500 Subject: [PATCH 168/221] Assign some more transient values to D --- alt/README.md | 2 +- alt/reg.py | 133 ++++++++++++++++++++++++++++++++++---------------- 2 files changed, 91 insertions(+), 44 deletions(-) diff --git a/alt/README.md b/alt/README.md index 8b530c6..12535b5 100644 --- a/alt/README.md +++ b/alt/README.md @@ -54,7 +54,7 @@ replaces certain function calls with lower-overhead "reduced" alternatives. | [alt/eight.py](eight.py) | 1,032 (-18%) | _same_ | +100% | +100% | | [alt/big.py](big.py) | 1,448 (+14%) | ? | ? | ? | | [alt/lazy.py](lazy.py) | _same_ | 23,650 (-8%) | 37,300 (-10%) | 111,000 (-14%) | -| [alt/reg.py](reg.py) | _same_ | 19,800 (-23%) | 14,700 (-64%) | 57,700 (-55%) | +| [alt/reg.py](reg.py) | _same_ | 19,450 (-24%) | 14,450 (-65%) | 56,600 (-56%) | | [alt/reduce.py](reduce.py) | _same_ | 27,350 (+6.5%) | 20,300 (-51%) | _same_ | diff --git a/alt/reg.py b/alt/reg.py index 513bb7d..741bc2b 100755 --- a/alt/reg.py +++ b/alt/reg.py @@ -458,13 +458,7 @@ def flatten_expression(expr: jack_ast.Expression, force=True) -> Tuple[List[Stmt elif isinstance(expr, jack_ast.UnaryExpression): stmts, child_expr = flatten_expression(expr.expr) - if isinstance(child_expr, Const) and expr.op.symbol == "-": - # This is ok because this VM handles negative constant values - flat_expr = Const(-child_expr.value) - # elif isinstance(child_expr, Const) and expr.op.symbol == "~": - # TODO: figure out how to evaluate logical negation at compile time, accounting for word size... - else: - flat_expr = Unary(expr.op, child_expr) + flat_expr = Unary(expr.op, child_expr) else: raise Exception(f"Unknown expression: {expr}") @@ -956,7 +950,7 @@ def rewrite_value(value: Value) -> Value: return value def rewrite_expr(expr: Expr) -> Expr: - if isinstance(expr, (CallSub, Const, Location, Static)): + if isinstance(expr, (CallSub, Const, Location, Static, Temp)): return expr elif isinstance(expr, Local): return rewrite_value(expr) @@ -1047,35 +1041,19 @@ def visit_stmts(l: Local, stmts: Sequence[LiveStmt]) -> bool: def visit_stmt(l: Local, stmt: LiveStmt) -> bool: if isinstance(stmt.statement, If): - if any(is_translatable(s) for s in stmt.statement.when_true): return True + if l in refs(stmt.statement.value): return True + elif any(visit_stmt(l, s) for s in stmt.statement.when_true): return True elif (stmt.statement.when_false is not None - and any(is_translatable(s) for s in stmt.statement.when_false)): return True + and any(visit_stmt(l, s) for s in stmt.statement.when_false)): return True elif isinstance(stmt.statement, While): if l in refs(stmt.statement.value): return True - elif any(is_translatable(s) for s in stmt.statement.test): return True - elif any(is_translatable(s) for s in stmt.statement.body): return True - - if l in stmt.before: - # TODO: rewrite the expression - return is_translatable(stmt.statement) - # if isinstance(stmt.statement, Eval): - # # TODO: rewrite the expression - # return is_translatable(stmt.statement) - # elif isinstance(stmt.statement, IndirectWrite): - # # TODO: rewrite the expression - # return is_translatable(stmt.statement) - # elif isinstance(stmt.statement, Store): - # # TODO: rewrite the expression - # return is_translatable(stmt.statement) - # elif isinstance(stmt.statement, Return): - # # TODO: rewrite the expression - # return is_translatable(stmt.statement) - # elif isinstance(stmt.statement, Push): - # # TODO: rewrite the expression - # return is_translatable(stmt.statement) - # elif isinstance(stmt.statement, Discard): - # # TODO: rewrite the expression - # return is_translatable(stmt.statement) + elif any(visit_stmt(l, s) for s in stmt.statement.test): return True + elif any(visit_stmt(l, s) for s in stmt.statement.body): return True + elif l in stmt.before: + # Tricky: this little hack fails if the stmt is nested, so we just don't do this path + # if it's If/While + rewritten, = resolve_locals([stmt.statement], { Local(l): Temp(l) }, fail_on_unmapped=False) + return is_translatable(rewritten) else: return False @@ -1084,18 +1062,75 @@ def visit_stmt(l: Local, stmt: LiveStmt) -> bool: return { l for l in one_time if visit_stmts(l, stmts) } -def is_translatable(stmt: Stmt): +def is_translatable(stmt: Stmt) -> bool: """True if the statement can be translated to assembly without disturbing Temp values stored in D. The statement may contain unresolved Locals; they are assumed to represent Reg locations that will be assigned later. + + Note: If/While aren't handled here, because a) their test values are always transient """ - if isinstance(stmt, If): # and isinstance(stmt.value, Temp): - # Actually, probably the value is only ever a Temp if we're asking the question - return True - # TODO: what else? - else: + def translatable_expr(expr: Expr) -> bool: + if isinstance(expr, CallSub): + raise Exception("silly question: a CallSub can't refer to a one-time (or any) local") + elif isinstance(expr, Const): + # A constant should never in and of itself require D to be overwritten. + return True + elif isinstance(expr, (Local, Reg, Static)): + # These are all places values can be accessed from without touching D + return True + elif isinstance(expr, Temp): + # The temp location itself is trivially ok + return True + elif isinstance(expr, Location): + # Accessing a stack-allocated variable may involve overwriting D + return False + elif isinstance(expr, Binary): + # Tricky: a binary expression can take one of its arguments from D, in some cases. + # TODO: how far does this get us? + # return translatable_expr(expr.left) and translatable_expr(expr.right) + # FIXME: temporarily disable, until the code generator knows how to handle it + return False + elif isinstance(expr, Unary): + # *Not* and *negate* can be done in place + return translatable_expr(expr.value) + elif isinstance(expr, IndirectRead): + # The *read* isn't a problem + return translatable_expr(expr.address) + else: + raise Exception(f"Unexpected expr: {expr}") + + def a_only(expr: Expr) -> bool: + """True if the value of the expression can be constructed directly in A (see value_to_a).""" + return isinstance(expr, (Const, Reg, Local, Static)) + + if isinstance(stmt, Eval): + return translatable_expr(stmt.expr) + elif isinstance(stmt, IndirectWrite): + # The *value* can come from D, if the *address* doesn't need to touch it: + if a_only(stmt.address) and translatable_expr(stmt.value): + return True + # Or, the *address* can come from D, if the value can be constructed by the ALU: + elif translatable_expr(stmt.address) and isinstance(stmt.value, Const) and (-1 <= stmt.value.value <= 1): + return True + else: + return False + elif isinstance(stmt, Store): + # TODO: this could be ok if the code generator can construct the address without D, + # which it can, if the index is 0 or 1 (or even -1), or even some larger values + # with some additional cycles if it's till cheaper than copying to a Register and + # back (which is 4 instructions). return False + elif isinstance(stmt, (If, While)): + raise Exception("Not handled here") + elif isinstance(stmt, Return): + return stmt.expr is None or translatable_expr(stmt.expr) + elif isinstance(stmt, Push): + return translatable_expr(stmt.expr) + elif isinstance(stmt, Discard): + raise Exception("silly question: a CallSub can't refer to a one-time (or any) local") + else: + raise Exception(f"Unexpected stmt: {stmt}") def phase_two(ast: Subroutine, reg_count: int = 8) -> Subroutine: @@ -1403,7 +1438,12 @@ def handle_IndirectWrite(self, ast: IndirectWrite): imm = self.immediate(ast.value) if imm is not None: - self.value_to_a(ast.address) + if isinstance(ast.address, Temp): + # TODO: this is a little silly; the previous instruction could probably just + # load the value into A if we knew that's where it would be useful + self.asm.instr("A=D") + else: + self.value_to_a(ast.address) self.asm.instr(f"M={imm}") else: self._handle(ast.value) @@ -1847,7 +1887,12 @@ def unary_op(self, op: jack_ast.Op) -> str: return {"-": "-", "~": "!"}[op.symbol] def handle_IndirectRead(self, ast: IndirectRead): - self.value_to_a(ast.address) + if isinstance(ast.address, Temp): + # TODO: this is a little silly; the previous instruction could probably just + # load the value into A if we knew that's where it would be useful + self.asm.instr("A=D") + else: + self.value_to_a(ast.address) self.asm.instr("D=M") def immediate(self, ast: Expr) -> Optional[int]: @@ -1888,6 +1933,8 @@ def describe_expr(self, expr) -> str: return "copy" elif isinstance(expr, Static): return "copy" # Confusing? These "loads" are as efficient as reg-reg copies + elif isinstance(expr, Temp): + return "direct" elif isinstance(expr, Binary): return "binary" elif isinstance(expr, Unary): @@ -1924,7 +1971,7 @@ def value_to_a(self, ast: Union[Reg, Static, Const]): self.asm.instr(f"@{-ast.value}") self.asm.instr("A=-A") else: - raise Exception(f"Unknown Value: {ast}") + raise Exception(f"Unexpected Value: {ast}") def _handle(self, ast): self.__getattribute__(f"handle_{ast.__class__.__name__}")(ast) From 53dfb36e046efbc915c37be88af7604691d58fc7 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Mon, 26 Feb 2024 19:22:29 -0500 Subject: [PATCH 169/221] =?UTF-8?q?Force=20each=20statement=E2=80=99s=20co?= =?UTF-8?q?de=20generator=20to=20be=20explicit=20about=20handling=20Temp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- alt/reg.py | 46 +++++++++++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/alt/reg.py b/alt/reg.py index 741bc2b..38ec9de 100755 --- a/alt/reg.py +++ b/alt/reg.py @@ -1445,6 +1445,9 @@ def handle_IndirectWrite(self, ast: IndirectWrite): else: self.value_to_a(ast.address) self.asm.instr(f"M={imm}") + elif isinstance(ast.value, Temp): + self.value_to_a(ast.address) + self.asm.instr("M=D") else: self._handle(ast.value) self.value_to_a(ast.address) @@ -1542,7 +1545,8 @@ def handle_If(self, ast: If): end_label = self.asm.next_label("end") self.asm.start(f"if {_Expr_str(ast.value)} {ast.cmp} 0?") - self._handle(ast.value) + if not isinstance(ast.value, Temp): + self._handle(ast.value) self.asm.instr(f"@{end_label}") self.asm.instr(f"D;J{self.compare_op_neg(ast.cmp)}") @@ -1556,7 +1560,8 @@ def handle_If(self, ast: If): false_label = self.asm.next_label("false") self.asm.start(f"if/else {_Expr_str(ast.value)} {ast.cmp} 0?") - self._handle(ast.value) + if not isinstance(ast.value, Temp): + self._handle(ast.value) self.asm.instr(f"@{false_label}") self.asm.instr(f"D;J{self.compare_op_neg(ast.cmp)}") @@ -1593,7 +1598,8 @@ def handle_While(self, ast): self._handle(s) self.asm.start(f"while-test {_Expr_str(ast.value)} {ast.cmp} 0?") - self._handle(ast.value) + if not isinstance(ast.value, Temp): + self._handle(ast.value) self.asm.instr(f"@{body_label}") self.asm.instr(f"D;J{self.compare_op_pos(ast.cmp)}") @@ -1652,7 +1658,8 @@ def handle_Push(self, ast): self.asm.instr("A=M-1") self.asm.instr(f"M={imm}") else: - self._handle(ast.expr) + if not isinstance(ast.expr, Temp): + self._handle(ast.expr) if isinstance(ast.expr, CallSub): self.asm.start(f"push-result {_Expr_str(ast.expr)}") @@ -1790,12 +1797,12 @@ def handle_Static(self, ast: Static): self.asm.instr("D=M") def handle_Temp(self, ast: Temp): - """Yesssss. The contract is to load the value into D, and Temp means the value is - already in D when we get here. - """ - pass + raise Exception("Unsafe! Callers should explicitly handle Temp when they can do so safely.") def handle_Binary(self, ast: Binary): + # TODO: identify cases where (one of) the operands can safely come from Temp (which is to say, D), + # handle them, and fix the earlier pass to make that happen + left_symbol = self.symbol(ast.left) alu_op = self.binary_op_alu(ast.op) right_imm = self.immediate(ast.right) @@ -1811,9 +1818,18 @@ def handle_Binary(self, ast: Binary): self.asm.instr(f"D=M{-right_imm:+}") return elif alu_op is not None: - self._handle(ast.left) # D = left - self.value_to_a(ast.right) # A = right - self.asm.instr(f"D=D{alu_op}A") + if isinstance(ast.left, Temp): + # D = left (from previous instr) + self.value_to_a(ast.right) # A = right + self.asm.instr(f"D=D{alu_op}A") + elif isinstance(ast.right, Temp): + self.value_to_a(ast.left) # A = left + # D = right (from previous instr) + self.asm.instr(f"D=A{alu_op}D") + else: + self._handle(ast.left) # D = left + self.value_to_a(ast.right) # A = right + self.asm.instr(f"D=D{alu_op}A") return cmp_op = self.binary_op_cmp(ast.op) @@ -1833,7 +1849,10 @@ def handle_Binary(self, ast: Binary): # Note: if the right operand is -1,0,1, we shave a few cycles. An earlier phase should # take care of rewriting conditions into that form where possible. - self._handle(ast.left) # D = left + if isinstance(ast.left, Temp): + pass # D = left (from previous instr) + else: + self._handle(ast.left) # D = left if right_imm == 0: pass # comparing with zero, so D already has left - 0, effectively elif right_imm is not None and -1 <= right_imm <= 1: @@ -1880,7 +1899,8 @@ def handle_Unary(self, ast: Unary): self.asm.instr(f"D={self.unary_op(ast.op)}M") else: # Note: ~(Const) isn't being evaluated in the compiler yet, but should be. - self._handle(ast.value) + if not isinstance(ast.value, Temp): + self._handle(ast.value) self.asm.instr(f"D={self.unary_op(ast.op)}D") def unary_op(self, op: jack_ast.Op) -> str: From 8ce23e585f713d7ac83e8ec9a16c6a6ac929eda9 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Mon, 26 Feb 2024 20:00:57 -0500 Subject: [PATCH 170/221] Handle operands in D for basically all binary/comp ops --- alt/README.md | 2 +- alt/reg.py | 67 +++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 50 insertions(+), 19 deletions(-) diff --git a/alt/README.md b/alt/README.md index 12535b5..04074c8 100644 --- a/alt/README.md +++ b/alt/README.md @@ -54,7 +54,7 @@ replaces certain function calls with lower-overhead "reduced" alternatives. | [alt/eight.py](eight.py) | 1,032 (-18%) | _same_ | +100% | +100% | | [alt/big.py](big.py) | 1,448 (+14%) | ? | ? | ? | | [alt/lazy.py](lazy.py) | _same_ | 23,650 (-8%) | 37,300 (-10%) | 111,000 (-14%) | -| [alt/reg.py](reg.py) | _same_ | 19,450 (-24%) | 14,450 (-65%) | 56,600 (-56%) | +| [alt/reg.py](reg.py) | _same_ | 18,200 (-29%) | 12,450 (-70%) | 55,250 (-57%) | | [alt/reduce.py](reduce.py) | _same_ | 27,350 (+6.5%) | 20,300 (-51%) | _same_ | diff --git a/alt/reg.py b/alt/reg.py index 38ec9de..b40ed33 100755 --- a/alt/reg.py +++ b/alt/reg.py @@ -1086,11 +1086,17 @@ def translatable_expr(expr: Expr) -> bool: # Accessing a stack-allocated variable may involve overwriting D return False elif isinstance(expr, Binary): - # Tricky: a binary expression can take one of its arguments from D, in some cases. - # TODO: how far does this get us? - # return translatable_expr(expr.left) and translatable_expr(expr.right) - # FIXME: temporarily disable, until the code generator knows how to handle it - return False + # Tricky: a binary expression can take one or both of its arguments from D, in some cases. + if isinstance(expr.left, Temp) and isinstance(expr.right, Temp): + return True + elif isinstance(expr.left, Temp) and a_only(expr.right): + return True + elif a_only(expr.left) and isinstance(expr.right, Temp): + return True + else: + # print(f"unsafe: {_Expr_str(expr)}") + # return False + raise Exception("Doesn't happen?!") elif isinstance(expr, Unary): # *Not* and *negate* can be done in place return translatable_expr(expr.value) @@ -1404,14 +1410,16 @@ def handle_Eval(self, ast: Eval): self.asm.instr(f"@{symbol_name}") self.asm.instr(f"M=M{-right_imm:+}") else: - self._handle(ast.expr.right) # D = right + if not isinstance(ast.expr.right, Temp): + self._handle(ast.expr.right) # D = right self.asm.instr(f"@{symbol_name}") self.asm.instr(f"M=M{op}D") return elif isinstance(ast.expr, Binary) and symbol_name == self.symbol(ast.expr.right): op = self.binary_op_alu(ast.expr.op) if op is not None: - self._handle(ast.expr.left) # D = left + if not isinstance(ast.expr.left, Temp): + self._handle(ast.expr.left) # D = left self.asm.instr(f"@{symbol_name}") self.asm.instr(f"M=D{op}M") return @@ -1818,14 +1826,20 @@ def handle_Binary(self, ast: Binary): self.asm.instr(f"D=M{-right_imm:+}") return elif alu_op is not None: - if isinstance(ast.left, Temp): + left_in_d = isinstance(ast.left, Temp) + right_in_d = isinstance(ast.right, Temp) + if left_in_d and right_in_d: + # e.g. x = x + x + self.asm.instr("A=D") + self.asm.instr(f"D=D{alu_op}A") + elif left_in_d: # D = left (from previous instr) self.value_to_a(ast.right) # A = right self.asm.instr(f"D=D{alu_op}A") - elif isinstance(ast.right, Temp): + elif right_in_d: self.value_to_a(ast.left) # A = left # D = right (from previous instr) - self.asm.instr(f"D=A{alu_op}D") + self.asm.instr(f"D=A{alu_op}D") # Note reversed operands (for -) else: self._handle(ast.left) # D = left self.value_to_a(ast.right) # A = right @@ -1849,18 +1863,35 @@ def handle_Binary(self, ast: Binary): # Note: if the right operand is -1,0,1, we shave a few cycles. An earlier phase should # take care of rewriting conditions into that form where possible. - if isinstance(ast.left, Temp): - pass # D = left (from previous instr) + # Now it breaks down into 6 possible cases, depending on whether there's an immediate + # on the right, and whether either operand is in D: + if right_imm is not None: + if isinstance(ast.left, Temp): + pass # D = left (from previous instr) + else: + self._handle(ast.left) # D = left + + if right_imm == 0: + pass # D = left - 0 (with no additional work) + else: + self.asm.instr(f"D=D{-right_imm:+d}") # D = left - right (so, positive if left > right) + + elif isinstance(ast.left, Temp): + # D = left (from previous instr) + self.value_to_a(ast.right) # A = right + self.asm.instr("D=D-A") # D = left - right (so, positive if left > right) + + elif isinstance(ast.right, Temp): + # D = right (from previous instr) + self.value_to_a(ast.left) # A = left + self.asm.instr("D=A-D") # D = left - right (so, positive if left > right) + else: self._handle(ast.left) # D = left - if right_imm == 0: - pass # comparing with zero, so D already has left - 0, effectively - elif right_imm is not None and -1 <= right_imm <= 1: - self.asm.instr(f"D=D{-right_imm:+d}") # D = left - right (so, positive if left > right) - else: self.value_to_a(ast.right) # A = right self.asm.instr("D=D-A") # D = left - right (so, positive if left > right) + self.asm.instr(f"@{true_label}") self.asm.instr(f"D;J{cmp_op}") @@ -1873,7 +1904,7 @@ def handle_Binary(self, ast: Binary): self.asm.label(end_label) return - raise Exception(f"TODO: {ast}") + raise Exception(f"Unexpected expr: {_Expr_str(ast)}") def binary_op_alu(self, op: jack_ast.Op) -> Optional[str]: return { From 2a137e02bc794643117d55205e1c4fee0459c5c6 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Tue, 27 Feb 2024 10:10:58 -0500 Subject: [PATCH 171/221] Simplify negative constants under binary expressions for trivial savings --- alt/reg.py | 17 +++++++++++++++-- alt/test_reg.py | 6 ++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/alt/reg.py b/alt/reg.py index b40ed33..40ed565 100755 --- a/alt/reg.py +++ b/alt/reg.py @@ -521,11 +521,14 @@ def simplify_constant(x: jack_ast.Expression) -> Optional[int]: return None if isinstance(expr, jack_ast.BinaryExpression): + left = simplify_expression(expr.left) + right = simplify_expression(expr.right) + # TODO: this transformation is the same as the standard compiler; share that code? if expr.op.symbol == "*": - return jack_ast.SubroutineCall("Math", None, "multiply", [expr.left, expr.right]) + return jack_ast.SubroutineCall("Math", None, "multiply", [left, right]) elif expr.op.symbol == "/": - return jack_ast.SubroutineCall("Math", None, "divide", [expr.left, expr.right]) + return jack_ast.SubroutineCall("Math", None, "divide", [left, right]) elif expr.op.symbol in ("<", ">", "=", "<=", ">=", "!="): def invert_cmp(cmp: Cmp) -> Cmp: """Reverse the operand order.""" @@ -541,6 +544,8 @@ def invert_cmp(cmp: Cmp) -> Cmp: expr.left)) elif simple_right is not None: # Constant on the right; match some common cases: + # The idea is to compare with 0 whenever possible, because that's what the chip + # actually provides directly. zero = jack_ast.IntegerConstant(0) if expr.op.symbol == "<" and simple_right == 1: return jack_ast.BinaryExpression(expr.left, jack_ast.Op("<="), zero) @@ -554,7 +559,15 @@ def invert_cmp(cmp: Cmp) -> Cmp: return jack_ast.BinaryExpression(expr.left, expr.op, jack_ast.IntegerConstant(simple_right)) + else: + return jack_ast.BinaryExpression(left, expr.op, right) + + elif (isinstance(expr, jack_ast.UnaryExpression) + and expr.op.symbol == "-" + and isinstance(expr.expr, jack_ast.IntegerConstant)): + return jack_ast.IntegerConstant(-expr.expr.value) + # Nothing matched: return expr diff --git a/alt/test_reg.py b/alt/test_reg.py index abe7495..0dadbbe 100755 --- a/alt/test_reg.py +++ b/alt/test_reg.py @@ -18,6 +18,12 @@ def tilde(exp): return jack_ast.UnaryExpression(jack_ast.Op("~"), exp) const = jack_ast.IntegerConstant def neg(exp): return jack_ast.UnaryExpression(jack_ast.Op("-"), exp) + assert (simplify_expression(neg(const(1))) + == const(-1)) + + assert (simplify_expression(binary(x, op("+"), neg(const(1)))) + == binary(x, op("+"), const(-1))) + assert (simplify_expression(binary(x, op("<"), const(0))) == binary(x, op("<"), const(0))) From f9de6201f4207ddf2f86bf1b5ae67ae510782d0a Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Tue, 27 Feb 2024 10:11:28 -0500 Subject: [PATCH 172/221] Disable very verbose logging --- alt/reg.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/alt/reg.py b/alt/reg.py index 40ed565..2b5cb79 100755 --- a/alt/reg.py +++ b/alt/reg.py @@ -61,9 +61,9 @@ Possibly makes tracing/debugging confusing or useless in these functions. """ -PRINT_LIVENESS = True +PRINT_LIVENESS = False """Print each function before assigning variables to registers. -Each statement is anootated with the set of variables which are 'live' at the beginning of +Each statement is annotated with the set of variables which are 'live' at the beginning of that statement. A live variable contains a value that will be used later; it must nt be overwritten at that point.""" From 7d10a830752812202cfdc221de1ad46490a7d058 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Tue, 27 Feb 2024 12:33:13 -0500 Subject: [PATCH 173/221] Tighten code slightly in multiply() and push() --- alt/scheme/Interpreter.jack | 44 +++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/alt/scheme/Interpreter.jack b/alt/scheme/Interpreter.jack index f4c725e..ad74acd 100644 --- a/alt/scheme/Interpreter.jack +++ b/alt/scheme/Interpreter.jack @@ -545,28 +545,40 @@ class Interpreter { Does that mean it's good? Maybe, but it probably works. */ function int multiply(int x, int y) { + var int localX, localY; var boolean neg; var int tmp; var int sum, shiftedX, shiftedBit; + // local copies for quick access + let localX = x; + let localY = y; + // Get sign, then take absolute values (inline to save fn calls): - if ((x < 0) = (y < 0)) { let neg = false; } else { let neg = true; } - if (y < 0) { let y = -y; } - if (x < 0) { let x = -x; } + // Note: testing each operand only once to minimize branching. + let neg = false; + if (localY < 0) { + let localY = -localY; + let neg = ~neg; + } + if (localX < 0) { + let localX = -localX; + let neg = ~neg; + } // Put the smaller (abs.) value in y (because this is O(log y)): - if (x < y) { - let tmp = y; - let y = x; - let x = tmp; + if (localX < localY) { + let tmp = localY; + let localY = localX; + let localX = tmp; } let sum = 0; - let shiftedX = x; + let shiftedX = localX; let shiftedBit = 1; - while ((shiftedBit > 0) & ~(shiftedBit > y)) { - if (y & shiftedBit) { + while ((shiftedBit > 0) & ~(shiftedBit > localY)) { + if (localY & shiftedBit) { let sum = sum + shiftedX; } let shiftedX = shiftedX + shiftedX; @@ -597,13 +609,11 @@ class Interpreter { // let stack = Interpreter.alloc(obj, stack, 0); // pair-type // Avoid function call overhead by doing the allocation directly here: - var Rib r; - let r = nextRib; - let r[0] = obj; - let r[1] = stack; - let r[2] = 0; // pair type - let nextRib = nextRib + 3; - let stack = r; + let nextRib[0] = obj; + let nextRib[1] = stack; + let nextRib[2] = 0; // pair type + let stack = nextRib; + let nextRib = nextRib+3; return; } From 2ccbe76ac00ba99d3b540eb49c000fbe26801d28 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Tue, 27 Feb 2024 12:34:04 -0500 Subject: [PATCH 174/221] Save an instruction when doubling a register --- alt/reg.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/alt/reg.py b/alt/reg.py index 2b5cb79..fc44f62 100755 --- a/alt/reg.py +++ b/alt/reg.py @@ -1406,7 +1406,16 @@ def handle_Eval(self, ast: Eval): symbol_name = self.symbol(ast.dest) # Do the update in-place if possible: - if isinstance(ast.expr, Binary) and symbol_name == self.symbol(ast.expr.left): + if (isinstance(ast.expr, Binary) + and symbol_name == self.symbol(ast.expr.left) and symbol_name == self.symbol(ast.expr.right)): + # This pretty much only handles x = x + x, but that occurs the in hot loop in multiply + op = self.binary_op_alu(ast.expr.op) + if op is not None: + self.asm.instr(f"@{symbol_name}") + self.asm.instr(f"D=M") + self.asm.instr(f"M=M{op}D") + return + elif isinstance(ast.expr, Binary) and symbol_name == self.symbol(ast.expr.left): op = self.binary_op_alu(ast.expr.op) if op is not None: right_imm = self.immediate(ast.expr.right) From 4237108316c21bd4363dbb9d3e417c271fc0b432 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Tue, 27 Feb 2024 13:40:04 -0500 Subject: [PATCH 175/221] =?UTF-8?q?Add=20a=20=E2=80=9CscreenAddr=E2=80=9D?= =?UTF-8?q?=20primitive,=20for=2012%=20speedup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because it’s easy to add a primitive now, and because it saves heap space as well as cycles. --- alt/scheme/Interpreter.jack | 48 ++++++++++++++++++++++++++++++++--- alt/scheme/example/output.scm | 22 +++++++++++++--- alt/scheme/inspector.py | 1 + 3 files changed, 65 insertions(+), 6 deletions(-) diff --git a/alt/scheme/Interpreter.jack b/alt/scheme/Interpreter.jack index ad74acd..21574fa 100644 --- a/alt/scheme/Interpreter.jack +++ b/alt/scheme/Interpreter.jack @@ -525,10 +525,18 @@ class Interpreter { } } else { // opcode >= 22 - // 22 - // halt + if (opcode < 23) { + // 22 + // halt - do Interpreter.halt(); + do Interpreter.halt(); + } + else { + // 23 + // screenAddr :: x y -- address of character at column x, line y + + do Interpreter.screenAddress(); + } } } } @@ -594,6 +602,40 @@ class Interpreter { } } + /* + Cheat: calculate the address of a character in the screen buffer, using + hand-coded "shift" and adds to multiply by 80, and without any allocation. + + Doing its own (interpreter) stack manipulation to keep variables local and avoid arguments + on the (Jack) stack, and doing it by hand so this can be a leaf function. + + TODO: implement function inlining and just use pop/peek/replace + */ + function Array screenAddress() { + var int x, y, acc0, acc1, acc2, acc3, acc4, acc5, acc6, addr; + + // let y = Interpreter.pop(); + let y = stack[0]; + let stack = stack[1]; + // let x = Interpreter.pop(); + let x = stack[0]; // i.e. peek() + + // 80x = 8*(4x + x) = 2(2(2(2(2(2x) + x)))) + let acc0 = y + y; // 2y + let acc1 = acc0 + acc0; // 4y + let acc2 = acc1 + y; // 5y + let acc3 = acc2 + acc2; // 10y + let acc4 = acc3 + acc3; // 20y + let acc5 = acc4 + acc4; // 40y + let acc6 = acc5 + acc5; // 80y + + let addr = 2048 + acc6 + x; + // do Interpreter.push(addr); + let stack[0] = addr; // replace(); + + return; + } + /** Allocate a rib on the heap, filling in the three fields. */ function Rib alloc(int x, int y, int z) { var Rib r; diff --git a/alt/scheme/example/output.scm b/alt/scheme/example/output.scm index cf5b7f2..e5b77ea 100644 --- a/alt/scheme/example/output.scm +++ b/alt/scheme/example/output.scm @@ -1,7 +1,10 @@ (define poke (rib 21 0 1)) -(define screen 2048) -(define (drawchar x y c) (poke (+ screen (+ x (* 80 y))) c)) +;; (define screen 2048) +;; (define (drawchar x y c) (poke (+ screen (+ x (* 80 y))) c)) + +(define screenAddr (rib 23 0 1)) +(define (drawchar x y c) (poke (screenAddr x y) c)) ;; First, ABCD in the corners of the screen: (drawchar 0 0 65) @@ -56,4 +59,17 @@ (+ y 4) (+ h x))))))) -;; 1.1M cycles and 40% of the heap to complete +;; 1.1M cycles and 40% of the heap to complete (assembly interpreter) + +;; 5.3M cycles and 38.4% of the heap (Jack interpreter) +;; 3.6M cycles after optimizing leaf functions in reg.py +;; 3.5M cycles after collapsing stores with simple expressions +;; 3.3M cycles after unifying codegen for registers and statics +;; [2.9M cycles after totally overhauling flattening (and probably introducing lots of bugs)] +;; 3.1M cycles after reducing use of arguments/locals in getTarget and handlePrimitive +;; 3.0M cycles after splitting locals in main() so most are in registers +;; 2.9M cycles after splitting savedProc from proc for dispatch +;; 2.8M cycles after assigning test values for if/while to D +;; 2.5M cycles after assigning memory read addresses to D +;; 2.4M cycles after assigning sources for binary/comp exprs to D +;; 2.1M cycles and 36% of the heap after adding a "screenAddress" primitive diff --git a/alt/scheme/inspector.py b/alt/scheme/inspector.py index 5e63238..b8588d3 100644 --- a/alt/scheme/inspector.py +++ b/alt/scheme/inspector.py @@ -202,4 +202,5 @@ def list_str(lst): 20: "peek", 21: "poke", 22: "halt", + 23: "screenAddr", } \ No newline at end of file From 6883e33f8f9fbee9cec6e8cae85b37ab36645814 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Tue, 27 Feb 2024 13:40:27 -0500 Subject: [PATCH 176/221] Manually inline calls to pop() --- alt/scheme/Interpreter.jack | 39 +++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/alt/scheme/Interpreter.jack b/alt/scheme/Interpreter.jack index 21574fa..603dd92 100644 --- a/alt/scheme/Interpreter.jack +++ b/alt/scheme/Interpreter.jack @@ -251,7 +251,7 @@ class Interpreter { function void handlePrimitive(int opcode_arg) { var int opcode; var int x, y, z; - var Rib tmp; + var Rib tmp, tmp21; // Make a local copy, which will end up in a register, avoiding // 4/5 loads from the stack every time: @@ -412,7 +412,10 @@ class Interpreter { // Note: one rib becomes garbage (top entry of stack) - let y = Interpreter.pop(); + // let y = Interpreter.pop(); + let y = stack[0]; + let stack = stack[1]; + let x = stack[0]; if (x = y) { let stack[0] = ribTrue; @@ -426,7 +429,10 @@ class Interpreter { // <: x y -- bool(x < y) // Note: one rib becomes garbage: - let y = Interpreter.pop(); + // let y = Interpreter.pop(); + let y = stack[0]; + let stack = stack[1]; + let x = stack[0]; // Update second stack entry in place: if (x < y) { @@ -443,7 +449,10 @@ class Interpreter { // +: x y -- (x + y) // Note: one rib becomes garbage: - let y = Interpreter.pop(); + // let y = Interpreter.pop(); + let y = stack[0]; + let stack = stack[1]; + let x = stack[0]; // Update second stack entry in place: let stack[0] = x + y; @@ -453,7 +462,10 @@ class Interpreter { // -: x y -- (x - y) // Note: one rib becomes garbage: - let y = Interpreter.pop(); + // let y = Interpreter.pop(); + let y = stack[0]; + let stack = stack[1]; + let x = stack[0]; // Update second stack entry in place: let stack[0] = x - y; @@ -471,8 +483,12 @@ class Interpreter { // * :: x y -- (x * y) // Note: one rib becomes garbage: - let y = Interpreter.pop(); + // let y = Interpreter.pop(); + let y = stack[0]; + let stack = stack[1]; + let x = stack[0]; + // Update second stack entry in place: let stack[0] = Interpreter.multiply(x, y); } @@ -515,11 +531,14 @@ class Interpreter { // poke :: x y -- y (and write the value y at RAM[x]) // Note: one rib becomes garbage: - let y = Interpreter.pop(); + // TODO: inline it + // let y = Interpreter.pop(); + let y = stack[0]; + let stack = stack[1]; + + let tmp21 = stack[0]; + let tmp21[0] = y; - let x = stack[0]; - let tmp = 0; - let tmp[x] = y; // Update the second stack entry in place let stack[0] = y; } From 4034db98c867a0792e49798e964f4f133a8a561e Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Tue, 27 Feb 2024 14:13:51 -0500 Subject: [PATCH 177/221] More manual inlining in Jack interpreter --- alt/scheme/Interpreter.jack | 25 ++++++++++++++++++++----- alt/scheme/example/output.scm | 1 + 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/alt/scheme/Interpreter.jack b/alt/scheme/Interpreter.jack index 603dd92..89b7509 100644 --- a/alt/scheme/Interpreter.jack +++ b/alt/scheme/Interpreter.jack @@ -231,19 +231,34 @@ class Interpreter { /** Pop numArgs objects from the stack, assembling them into a new stack (in reverse order), on top of the just-allocated continuation rib. + + Note aggressive manual inlining here to reduce call overhead because this + is part of the most expensive code path. */ function Rib wrangleClosureParams(Rib proc, Rib cont) { var Rib code; - var int numArgs, i; + var int numArgs; + var int x; var Rib newStack; let code = proc[0]; let numArgs = code[0]; - let i = 0; let newStack = cont; - while (i < numArgs) { - let newStack = Interpreter.alloc(Interpreter.pop(), newStack, 0); - let i = i + 1; + while (numArgs > 0) { + // let x = Interpreter.pop(); + let x = stack[0]; + let stack = stack[1]; + + // Note: can't re-use the stack entries, because they seem to be shared + // with captured environment in some cases: + // let newStack = Interpreter.alloc(x, newStack, 0); + let nextRib[0] = x; + let nextRib[1] = newStack; + let nextRib[2] = 0; + let newStack = nextRib; + let nextRib = nextRib + 3; + + let numArgs = numArgs - 1; } return newStack; } diff --git a/alt/scheme/example/output.scm b/alt/scheme/example/output.scm index e5b77ea..aa879a0 100644 --- a/alt/scheme/example/output.scm +++ b/alt/scheme/example/output.scm @@ -73,3 +73,4 @@ ;; 2.5M cycles after assigning memory read addresses to D ;; 2.4M cycles after assigning sources for binary/comp exprs to D ;; 2.1M cycles and 36% of the heap after adding a "screenAddress" primitive +;; 1.8M cycles after inlining calls in "call" sequence (wrangleClosureParams) From 89232f4f3765de714dfaeb16db39a48b38bd02d3 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Tue, 27 Feb 2024 21:37:09 -0500 Subject: [PATCH 178/221] Add a function pointer mechanism: Jack.symbol and Jack.invoke --- alt/reg.py | 155 +++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 127 insertions(+), 28 deletions(-) diff --git a/alt/reg.py b/alt/reg.py index fc44f62..49153a5 100755 --- a/alt/reg.py +++ b/alt/reg.py @@ -39,8 +39,32 @@ These changes mean that the debug/trace logging done by some test cases doesn't always show the correct arguments, locals, return addresses, and result values. + +Extensions: + +Two additional primitives are provided, to support dynamic dispatch (that is, storing the address +of a function or method and invoking it later): + +- Jack.symbol(): address of the given label. +- Jack.invoke(ptr, ...): call the function/method referred to by the pointer. + +For example, this code sequence: + +``` +var int fptr; // the type doesn't matter +let fooPtr = Jack.symbol("main.foo"); +... +do Jack.invoke(fooPtr, "bar"); +``` + +has the same effect as this simple call: + +``` +do Main.foo("bar"); +``` """ + from collections import Counter import itertools from os import name @@ -135,7 +159,7 @@ class Push(NamedTuple): class Discard(NamedTuple): """Call a subroutine, then pop the stack, discarding the result.""" - expr: "CallSub" + expr: Union["CallSub", "Invoke"] Stmt = Union[Eval, IndirectWrite, Store, If, While, Push, Discard] @@ -193,7 +217,18 @@ class IndirectRead(NamedTuple): """Get the value given an address, aka peek().""" address: "Value" -Expr = Union[CallSub, Const, Local, Location, Reg, Static, Binary, Unary, IndirectRead] +# Extensions: + +class Symbol(NamedTuple): + """Address of an arbitrary symbol.""" + name: str + +class Invoke(NamedTuple): + """Call a subroutine given the address of its entry point.""" + ptr: "Value" + # numArgs? + +Expr = Union[CallSub, Const, Local, Location, Reg, Static, Binary, Unary, IndirectRead, Symbol, Invoke] class Subroutine(NamedTuple): @@ -435,20 +470,33 @@ def flatten_expression(expr: jack_ast.Expression, force=True) -> Tuple[List[Stmt flat_expr = IndirectRead(address_expr) elif isinstance(expr, jack_ast.SubroutineCall): - pairs = [flatten_expression(a, force=False) for a in expr.args] - arg_stmts = [s for ss, x in pairs for s in ss + [Push(x)]] - if expr.class_name is not None: - stmts = arg_stmts - flat_expr = CallSub(expr.class_name, expr.sub_name, len(expr.args)) - elif expr.var_name is not None: - instance_stmts, instance_expr = flatten_expression(jack_ast.VarRef(expr.var_name), force=False) - stmts = instance_stmts + [Push(instance_expr)] + arg_stmts - target_class = symbol_table.type_of(expr.var_name) - flat_expr = CallSub(target_class, expr.sub_name, len(expr.args) + 1) + if expr.class_name == "Jack" and expr.sub_name == "symbol": + assert len(expr.args) == 1 and isinstance(expr.args[0], jack_ast.StringConstant) + stmts = [] + flat_expr = Symbol(expr.args[0].value) + + elif expr.class_name == "Jack" and expr.sub_name == "invoke": + # TODO: handle arguments, when someone needsw them + assert len(expr.args) == 1 + target_stmts, target_expr = flatten_expression(expr.args[0]) + stmts = target_stmts + flat_expr = Invoke(target_expr) + else: - stmts = [Push(this_expr)] + arg_stmts - target_class = symbol_table.class_name - flat_expr = CallSub(target_class, expr.sub_name, len(expr.args) + 1) + pairs = [flatten_expression(a, force=False) for a in expr.args] + arg_stmts = [s for ss, x in pairs for s in ss + [Push(x)]] + if expr.class_name is not None: + stmts = arg_stmts + flat_expr = CallSub(expr.class_name, expr.sub_name, len(expr.args)) + elif expr.var_name is not None: + instance_stmts, instance_expr = flatten_expression(jack_ast.VarRef(expr.var_name), force=False) + stmts = instance_stmts + [Push(instance_expr)] + arg_stmts + target_class = symbol_table.type_of(expr.var_name) + flat_expr = CallSub(target_class, expr.sub_name, len(expr.args) + 1) + else: + stmts = [Push(this_expr)] + arg_stmts + target_class = symbol_table.class_name + flat_expr = CallSub(target_class, expr.sub_name, len(expr.args) + 1) elif isinstance(expr, jack_ast.BinaryExpression): left_stmts, left_expr = flatten_expression(expr.left) @@ -692,7 +740,7 @@ def analyze_while(stmt: While, live_at_end) -> Tuple[While, Set[str]]: elif isinstance(stmt, Push): read.update(refs(stmt.expr)) elif isinstance(stmt, Discard): - pass + read.update(refs(stmt.expr)) else: raise Exception(f"Unknown statement: {stmt}") @@ -715,6 +763,8 @@ def refs(expr: Expr) -> Set[str]: return refs(expr.value) elif isinstance(expr, IndirectRead): return refs(expr.address) + elif isinstance(expr, Invoke): + return refs(expr.ptr) else: return set() @@ -728,8 +778,8 @@ def need_saving(liveness: Sequence[LiveStmt]) -> Set[str]: result = set() for l in liveness: - if isinstance(l.statement, (Eval, Push, Discard)) and isinstance(l.statement.expr, CallSub): - result.update(l.before) + if isinstance(l.statement, (Eval, Push, Discard)) and isinstance(l.statement.expr, (CallSub, Invoke)): + result.update(l.during) elif isinstance(l.statement, If): result.update(need_saving(l.statement.when_true)) if l.statement.when_false is not None: @@ -783,6 +833,11 @@ def rewrite_expr(expr: Expr) -> Tuple[List[Stmt], Expr]: elif isinstance(expr, IndirectRead): stmts, address = rewrite_value(expr.address) return stmts, IndirectRead(address) + elif isinstance(expr, Symbol): + return [], expr + elif isinstance(expr, Invoke): + stmts, ptr = rewrite_value(expr.ptr) + return stmts, Invoke(ptr) else: raise Exception(f"Unknown Expr: {expr}") @@ -824,7 +879,8 @@ def rewrite_statement(stmt: Stmt) -> List[Stmt]: expr_stmts, expr = rewrite_expr(stmt.expr) return expr_stmts + [Push(expr)] elif isinstance(stmt, Discard): - return [stmt] + expr_stmts, expr = rewrite_expr(stmt.expr) + return expr_stmts + [Discard(expr)] else: raise Exception(f"Unknown Stmt: {stmt}") @@ -977,6 +1033,11 @@ def rewrite_expr(expr: Expr) -> Expr: elif isinstance(expr, IndirectRead): address = rewrite_value(expr.address) return IndirectRead(address) + elif isinstance(expr, Symbol): + return expr + elif isinstance(expr, Invoke): + ptr = rewrite_value(expr.ptr) + return Invoke(ptr) else: raise Exception(f"Unknown Expr: {expr}") @@ -1009,7 +1070,8 @@ def rewrite_statement(stmt: Stmt) -> Stmt: expr = rewrite_expr(stmt.expr) return Push(expr) elif isinstance(stmt, Discard): - return stmt + expr = rewrite_expr(stmt.expr) + return Discard(expr) else: raise Exception(f"Unknown Stmt: {stmt}") @@ -1116,6 +1178,11 @@ def translatable_expr(expr: Expr) -> bool: elif isinstance(expr, IndirectRead): # The *read* isn't a problem return translatable_expr(expr.address) + elif isinstance(expr, Symbol): + return True + elif isinstance(expr, Invoke): + # Need D to set up the return address + return False else: raise Exception(f"Unexpected expr: {expr}") @@ -1147,7 +1214,7 @@ def a_only(expr: Expr) -> bool: elif isinstance(stmt, Push): return translatable_expr(stmt.expr) elif isinstance(stmt, Discard): - raise Exception("silly question: a CallSub can't refer to a one-time (or any) local") + return translatable_expr(stmt.expr) else: raise Exception(f"Unexpected stmt: {stmt}") @@ -1303,7 +1370,7 @@ def translate_subroutine(self, subroutine_ast: Subroutine, class_name: str): def uses_stack(stmt: Stmt): if isinstance(stmt, Eval): - return isinstance(stmt.expr, CallSub) + return isinstance(stmt.expr, (CallSub, Invoke)) elif isinstance(stmt, IndirectWrite): return False elif isinstance(stmt, Store): @@ -1313,11 +1380,11 @@ def uses_stack(stmt: Stmt): elif isinstance(stmt, While): return any(uses_stack(s) for s in stmt.test + stmt.body) elif isinstance(stmt, Return): - return isinstance(stmt.expr, CallSub) # FIXME: does this happen? + return isinstance(stmt.expr, (CallSub, Invoke)) # FIXME: does this happen? elif isinstance(stmt, Push): return True elif isinstance(stmt, Discard): - return isinstance(stmt.expr, CallSub) + return isinstance(stmt.expr, (CallSub, Invoke)) else: raise Exception(f"Unknown Stmt: {stmt}") @@ -1702,10 +1769,17 @@ def handle_Push(self, ast): def handle_Discard(self, ast: Discard): # Note: now that results are passed in a register, there's no cleanup to do when # the result is not used. - self.call(ast.expr) + + if isinstance(ast.expr, CallSub): + self.call(ast.expr) + elif isinstance(ast.expr, Invoke): + self.invoke(ast.expr) + else: + raise Exception(f"Unexpected expr; {_Stmt_str(ast)}") self.asm.comment(f"ignore the result") + def compare_op_neg(self, cmp: Cmp): return { "=": "NE", @@ -1753,6 +1827,22 @@ def call(self, ast: CallSub): self.asm.label(return_label) + def invoke(self, ast: Invoke): + return_label = self.asm.next_label("return_address") + + self.asm.start(f"call (* {_Expr_str(ast.ptr)})") + + self.asm.instr(f"@{return_label}") + self.asm.instr("D=A") + self.asm.instr(f"@{RETURN_ADDRESS}") + self.asm.instr("M=D") + + self.value_to_a(ast.ptr) + self.asm.instr("0;JMP") + + self.asm.label(return_label) + + def handle_Const(self, ast: Const): if -1 <= ast.value <= 1: @@ -1830,9 +1920,6 @@ def handle_Temp(self, ast: Temp): raise Exception("Unsafe! Callers should explicitly handle Temp when they can do so safely.") def handle_Binary(self, ast: Binary): - # TODO: identify cases where (one of) the operands can safely come from Temp (which is to say, D), - # handle them, and fix the earlier pass to make that happen - left_symbol = self.symbol(ast.left) alu_op = self.binary_op_alu(ast.op) right_imm = self.immediate(ast.right) @@ -1968,6 +2055,10 @@ def handle_IndirectRead(self, ast: IndirectRead): self.value_to_a(ast.address) self.asm.instr("D=M") + def handle_Symbol(self, ast: Symbol): + self.asm.instr(f"@{ast.name}") + self.asm.instr("D=A") + def immediate(self, ast: Expr) -> Optional[int]: """If the expression is a constant which the ALU can take as an "immediate" operand (i.e. -1, 0, or 1), then unpack it. @@ -2014,6 +2105,10 @@ def describe_expr(self, expr) -> str: return "unary" elif isinstance(expr, IndirectRead): return "read" + elif isinstance(expr, Symbol): + return "symbol" + elif isinstance(expr, Invoke): + return "invoke" else: raise Exception(f"Unknown expr: {expr}") @@ -2196,6 +2291,10 @@ def _Expr_str(expr: Expr) -> str: return f"{expr.op.symbol} {_Expr_str(expr.value)}" elif isinstance(expr, IndirectRead): return f"mem[{_Expr_str(expr.address)}]" + elif isinstance(expr, Symbol): + return f"&{expr.name}" + elif isinstance(expr, Invoke): + return f"call (* {_Expr_str(expr.ptr)})" else: raise Exception(f"Unknown Expr: {expr}") From 1cd0dff8542ffd7f3908ff8bdb56241cf52154de Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Tue, 27 Feb 2024 21:38:08 -0500 Subject: [PATCH 179/221] Use function pointers to dispatch to primitive handlers --- alt/scheme/Interpreter.jack | 742 ++++++++++++++++++---------------- alt/scheme/example/output.scm | 1 + 2 files changed, 389 insertions(+), 354 deletions(-) diff --git a/alt/scheme/Interpreter.jack b/alt/scheme/Interpreter.jack index 89b7509..5422d1a 100644 --- a/alt/scheme/Interpreter.jack +++ b/alt/scheme/Interpreter.jack @@ -8,6 +8,9 @@ class Interpreter { static Rib pc; static Rib nextRib; + // Pointers to the functions that handle each primitive: + static Array handlers; + // Pre-allocated "proc" containing the "rib" primitive, which will be used to construct every // other primitive. static Rib ribRib; @@ -50,6 +53,8 @@ class Interpreter { do Interpreter.initSymbolTable(); + do Interpreter.initPrimitiveVectors(); + // Skip the bogus "main" instr: let pc = pc[2]; @@ -81,7 +86,7 @@ class Interpreter { else { // primitive - do Interpreter.handlePrimitive(proc[0]); + do Jack.invoke(handlers[proc[0]]); let cont = Interpreter.findContinuation(); @@ -120,7 +125,7 @@ class Interpreter { else { // primitive - do Interpreter.handlePrimitive(proc[0]); + do Jack.invoke(handlers[proc[0]]); let pc = pc[2]; } @@ -201,6 +206,387 @@ class Interpreter { return; } + + function void initPrimitiveVectors() { + // Just below the screen buffer + let handlers = 2048 - 32; + + let handlers[ 0] = Jack.symbol("interpreter.handleRib"); + let handlers[ 1] = Jack.symbol("interpreter.handleId"); + let handlers[ 2] = Jack.symbol("interpreter.handleArg1"); + let handlers[ 3] = Jack.symbol("interpreter.handleArg2"); + let handlers[ 4] = Jack.symbol("interpreter.handleClose"); + let handlers[ 5] = Jack.symbol("interpreter.handleRibQ"); + let handlers[ 6] = Jack.symbol("interpreter.handleField0"); + let handlers[ 7] = Jack.symbol("interpreter.handleField1"); + let handlers[ 8] = Jack.symbol("interpreter.handleField2"); + let handlers[ 9] = Jack.symbol("interpreter.handleField0_set"); + let handlers[10] = Jack.symbol("interpreter.handleField1_set"); + let handlers[11] = Jack.symbol("interpreter.handleField2_set"); + let handlers[12] = Jack.symbol("interpreter.handleEqvQ"); + let handlers[13] = Jack.symbol("interpreter.handleLt"); + let handlers[14] = Jack.symbol("interpreter.handlePlus"); + let handlers[15] = Jack.symbol("interpreter.handleMinus"); + let handlers[16] = Jack.symbol("interpreter.handleTimes"); + let handlers[17] = Jack.symbol("interpreter.handleUnimp"); // quotient + let handlers[18] = Jack.symbol("interpreter.handleGetchar"); + let handlers[19] = Jack.symbol("interpreter.handleUnimp"); // putchar + let handlers[20] = Jack.symbol("interpreter.handlePeek"); + let handlers[21] = Jack.symbol("interpreter.handlePoke"); + let handlers[22] = Jack.symbol("interpreter.handleHalt"); + let handlers[23] = Jack.symbol("interpreter.handleScreenAddr"); + + // Extra, just to simplify the dispatching logic: + let handlers[24] = Jack.symbol("interpreter.handleUnimp"); + let handlers[25] = Jack.symbol("interpreter.handleUnimp"); + let handlers[26] = Jack.symbol("interpreter.handleUnimp"); + let handlers[27] = Jack.symbol("interpreter.handleUnimp"); + let handlers[28] = Jack.symbol("interpreter.handleUnimp"); + let handlers[29] = Jack.symbol("interpreter.handleUnimp"); + let handlers[30] = Jack.symbol("interpreter.handleUnimp"); + let handlers[31] = Jack.symbol("interpreter.handleUnimp"); + + return; + } + + /* rib :: x y z -- rib(x, y, z) */ + function void handleRib() { + var int x, y, z; + var Rib tmp; + + // Note: one rib becomes garbage (top entry of stack) + + let z = stack[0]; + + let tmp = stack[1]; + let y = tmp[0]; + + // The entry holding x will be the new top of stack: + let stack = tmp[1]; + let x = stack[0]; + + // Now re-use the second entry's rib as the newly-constructed rib: + let tmp[0] = x; + let tmp[1] = y; + let tmp[2] = z; + let stack[0] = tmp; + + return; + } + + /* id :: x -- x */ + function void handleId() { + return; + } + + /* arg1 :: x y -- x") # i.e. "drop" */ + function void handleArg1() { + let stack = stack[1]; + + return; + } + + /* arg2 :: x y -- y */ + function void handleArg2() { + // TODO + do Interpreter.halt(); + + return; + } + + /* close :: x -- rib(x[0], stack, 1) */ + function void handleClose() { + var int x, y, z; + var Rib tmp; + + // Note: modifyinging the top entry on the stack in place, + // but allocating a new rib for the closure. + let tmp = stack[0]; + let stack[0] = Interpreter.alloc(tmp[0], stack[1], 1); + + return; + } + + /* rib? :: x -- bool(x is a rib) */ + function void handleRibQ() { + var int x, y, z; + var Rib tmp; + + let tmp = stack[0]; + if (Rib.isRib(tmp)) { + let stack[0] = ribTrue; + } + else { + let stack[0] = ribFalse; + } + + return; + } + + /* field0 :: rib(x, _, _) -- x */ + function void handleField0() { + var int x, y, z; + var Rib tmp; + + // No allocation: the top entry on the stack is updated in place + let tmp = stack[0]; + let stack[0] = tmp[0]; + + return; + } + + /* field1 :: rib(_, y, _) -- y */ + function void handleField1() { + var int x, y, z; + var Rib tmp; + + // No allocation: the top entry on the stack is updated in place + let tmp = stack[0]; + let stack[0] = tmp[1]; + + return; + } + + /* field2 :: rib(_, _, z) -- z */ + function void handleField2() { + var int x, y, z; + var Rib tmp; + + // No allocation: the top entry on the stack is updated in place + let tmp = stack[0]; + let stack[0] = tmp[2]; + + return; + } + + /* field0-set! :: rib(_, y, z) x -- x (and update the rib in place: rib(x, y, z)) */ + function void handleField0_set() { + var int x, y, z; + var Rib tmp; + + // Note: one rib becomes garbage (top entry of stack) + + let x = Interpreter.pop(); + let tmp = stack[0]; + let tmp[0] = x; + // Update the second entry on the stack in place: + let stack[0] = x; + + return; + } + + /* field1-set! :: rib(x, _, z) y -- y (and update the rib in place: rib(x, y, z)) */ + function void handleField1_set() { + var int x, y, z; + var Rib tmp; + + // Note: one rib becomes garbage (top entry of stack) + + let y = Interpreter.pop(); + let tmp = stack[0]; + let tmp[1] = y; + // Update the second entry on the stack in place: + let stack[0] = y; + + return; + } + + /* field2-set! :: rib(x, y, _) z -- z (and update the rib in place: rib(x, y, z)) */ + function void handleField2_set() { + var int x, y, z; + var Rib tmp; + + // Note: one rib becomes garbage (top entry of stack) + + let z = Interpreter.pop(); + let tmp = stack[0]; + let tmp[2] = z; + // Update the second entry on the stack in place: + let stack[0] = z; + + return; + } + + /* eqv? :: x y -- bool(x is identical to y) */ + function void handleEqvQ() { + var int x, y, z; + var Rib tmp; + + // Note: one rib becomes garbage (top entry of stack) + + // let y = Interpreter.pop(); + let y = stack[0]; + let stack = stack[1]; + + let x = stack[0]; + if (x = y) { + let stack[0] = ribTrue; + } + else { + let stack[0] = ribFalse; + } + + return; + } + + /* < :: x y -- bool(x < y) */ + function void handleLt() { + var int x, y, z; + var Rib tmp; + + // Note: one rib becomes garbage: + // let y = Interpreter.pop(); + let y = stack[0]; + let stack = stack[1]; + + let x = stack[0]; + // Update second stack entry in place: + if (x < y) { + let stack[0] = ribTrue; + } + else { + let stack[0] = ribFalse; + } + + return; + } + + /* + :: x y -- x + y */ + function void handlePlus() { + var int x, y, z; + var Rib tmp; + + // Note: one rib becomes garbage: + // let y = Interpreter.pop(); + let y = stack[0]; + let stack = stack[1]; + + let x = stack[0]; + // Update second stack entry in place: + let stack[0] = x + y; + + return; + } + + /* - :: x y -- x - y */ + function void handleMinus() { + var int x, y, z; + var Rib tmp; + + // Note: one rib becomes garbage: + // let y = Interpreter.pop(); + let y = stack[0]; + let stack = stack[1]; + + let x = stack[0]; + // Update second stack entry in place: + let stack[0] = x - y; + + return; + } + + /* * :: x y -- x * y */ + function void handleTimes() { + var int x, y, z; + var Rib tmp; + + // Note: one rib becomes garbage: + // let y = Interpreter.pop(); + let y = stack[0]; + let stack = stack[1]; + + let x = stack[0]; + + // Update second stack entry in place: + let stack[0] = Interpreter.multiply(x, y); + + return; + } + + /* getchar :: -- (blocks until a key is pressed) */ + function void handleGetchar() { + var int x, y, z; + var Rib tmp; + + // TODO + do Interpreter.halt(); + } + + /* peek :: x -- RAM[x] */ + function void handlePeek() { + var int x, y, z; + var Rib tmp; + + // TODO + do Interpreter.halt(); + } + + /* poke :: x y -- y (and write the value y at RAM[x]) */ + function void handlePoke() { + var int y; + var Array tmp; + + // Note: one rib becomes garbage: + // TODO: inline it + // let y = Interpreter.pop(); + let y = stack[0]; + let stack = stack[1]; + + let tmp = stack[0]; + let tmp[0] = y; + + // Update the second stack entry in place + let stack[0] = y; + + return; + } + + /* halt :: -- (no more instructions are executed) */ + function void handleHalt() { + do Interpreter.halt(); + } + + /* screenAddr :: x y -- address of character at column x, line y + + Cheat: calculate the address of a character in the screen buffer, using + hand-coded "shift" and adds to multiply by 80, and without any allocation. + + Doing its own (interpreter) stack manipulation to keep variables local and avoid arguments + on the (Jack) stack, and doing it by hand so this can be a leaf function. + + TODO: implement function inlining and just use pop/peek/replace + */ + function void handleScreenAddr() { + var int x, y, acc0, acc1, acc2, acc3, acc4, acc5, acc6, addr; + + // let y = Interpreter.pop(); + let y = stack[0]; + let stack = stack[1]; + // let x = Interpreter.pop(); + let x = stack[0]; // i.e. peek() + + // 80x = 8*(4x + x) = 2(2(2(2(2(2x) + x)))) + let acc0 = y + y; // 2y + let acc1 = acc0 + acc0; // 4y + let acc2 = acc1 + y; // 5y + let acc3 = acc2 + acc2; // 10y + let acc4 = acc3 + acc3; // 20y + let acc5 = acc4 + acc4; // 40y + let acc6 = acc5 + acc5; // 80y + + let addr = 2048 + acc6 + x; + // do Interpreter.push(addr); + let stack[0] = addr; // replace(); + + return; + } + + /* Handler for any unused primitive code. */ + function void handleUnimp() { + // TODO + do Interpreter.halt(); + } + /** Decode the "y" value from a jump/call, set, or get instruction, and return the rib that contains the target, which might be a stack entry or a symbol. In either case, the actual @@ -263,324 +649,6 @@ class Interpreter { return newStack; } - function void handlePrimitive(int opcode_arg) { - var int opcode; - var int x, y, z; - var Rib tmp, tmp21; - - // Make a local copy, which will end up in a register, avoiding - // 4/5 loads from the stack every time: - let opcode = opcode_arg; - - // Note: What you really want here is a computed jump (i.e. a switch construct.) - // Failing that, we make do with a binary search, effectively, with 5 branches - // to examine the 5 bits of the opcode. - - if (opcode < 16) { - if (opcode < 8) { - if (opcode < 4) { - if (opcode < 2) { - if (opcode < 1) { - // 0 - // rib :: x y z -- rib(x, y, z) - // Note: one rib becomes garbage (top entry of stack) - - let z = stack[0]; - - let tmp = stack[1]; - let y = tmp[0]; - - // The entry holding x will be the new top of stack: - let stack = tmp[1]; - let x = stack[0]; - - // Now re-use the second entry's rib as the newly-constructed rib: - let tmp[0] = x; - let tmp[1] = y; - let tmp[2] = z; - let stack[0] = tmp; - } - else { - // 1 - // id :: x -- x - - // Literally nothing to see here - } - } - else { // opcode >= 2 - if (opcode < 3) { - // 2 - // arg1 :: x y -- x (i.e. "drop") - - let stack = stack[1]; - } - else { - // 3 - // arg2 :: x y -- y - - // TODO - let tty[0] = opcode; // DEBUG - do Interpreter.halt(); - } - } - } - else { // opcode >= 4 - if (opcode < 6) { - if (opcode < 5) { - // 4 - // close :: x -- rib(x[0], stack, 1) - - // Note: modifyinging the top entry on the stack in place, - // but allocating a new rib for the closure. - let tmp = stack[0]; - let stack[0] = Interpreter.alloc(tmp[0], stack[1], 1); - } - else { - // 5 - // rib? :: x -- bool(x is a rib) - - let tmp = stack[0]; - if (Rib.isRib(tmp)) { - let stack[0] = ribTrue; - } - else { - let stack[0] = ribFalse; - } - } - } - else { // opcode >= 6 - if (opcode < 7) { - // 6 - // field0 :: rib(x, _, _) -- x - - // No allocation: the top entry on the stack is updated in place - let tmp = stack[0]; - let stack[0] = tmp[0]; - } - else { - // 7 - // field1 :: rib(_, y, _) -- y - - // No allocation: the top entry on the stack is updated in place - let tmp = stack[0]; - let stack[0] = tmp[1]; - } - } - } - } - else { // opcode >= 8 - if (opcode < 12) { - if (opcode < 10) { - if (opcode < 9) { - // 8 - // field2 :: rib(_, _, z) -- z - - // No allocation: the top entry on the stack is updated in place - let tmp = stack[0]; - let stack[0] = tmp[2]; - } - else { - // 9 - // field0-set! :: rib(_, y, z) x -- x (and update the rib in place: rib(x, y, z)) - - // Note: one rib becomes garbage (top entry of stack) - - let x = Interpreter.pop(); - let tmp = stack[0]; - let tmp[0] = x; - // Update the second entry on the stack in place: - let stack[0] = x; - } - } - else { // opcode >= 10 - if (opcode < 11) { - // 10 - // field1-set! :: rib(x, _, z) y -- y (and update the rib in place: rib(x, y, z)) - - // Note: one rib becomes garbage (top entry of stack) - - let y = Interpreter.pop(); - let tmp = stack[0]; - let tmp[1] = y; - // Update the second entry on the stack in place: - let stack[0] = y; - } - else { - // 11 - // field2-set! :: rib(x, y, _) z -- z (and update the rib in place: rib(x, y, z)) - - // Note: one rib becomes garbage (top entry of stack) - - let z = Interpreter.pop(); - let tmp = stack[0]; - let tmp[2] = z; - // Update the second entry on the stack in place: - let stack[0] = z; - } - } - } - else { // opcode >= 12 - if (opcode < 14) { - if (opcode < 13) { - // 12 - // eqv? :: x y -- bool(x is identical to y) - - // Note: one rib becomes garbage (top entry of stack) - - // let y = Interpreter.pop(); - let y = stack[0]; - let stack = stack[1]; - - let x = stack[0]; - if (x = y) { - let stack[0] = ribTrue; - } - else { - let stack[0] = ribFalse; - } - } - else { - // 13 - // <: x y -- bool(x < y) - - // Note: one rib becomes garbage: - // let y = Interpreter.pop(); - let y = stack[0]; - let stack = stack[1]; - - let x = stack[0]; - // Update second stack entry in place: - if (x < y) { - let stack[0] = ribTrue; - } - else { - let stack[0] = ribFalse; - } - } - } - else { // opcode >= 14 - if (opcode < 15) { - // 14 - // +: x y -- (x + y) - - // Note: one rib becomes garbage: - // let y = Interpreter.pop(); - let y = stack[0]; - let stack = stack[1]; - - let x = stack[0]; - // Update second stack entry in place: - let stack[0] = x + y; - } - else { - // 15 - // -: x y -- (x - y) - - // Note: one rib becomes garbage: - // let y = Interpreter.pop(); - let y = stack[0]; - let stack = stack[1]; - - let x = stack[0]; - // Update second stack entry in place: - let stack[0] = x - y; - } - } - } - } - } - else { // opcode >= 16 - if (opcode < 24) { - if (opcode < 20) { - if (opcode < 18) { - if (opcode < 17) { - // 16 - // * :: x y -- (x * y) - - // Note: one rib becomes garbage: - // let y = Interpreter.pop(); - let y = stack[0]; - let stack = stack[1]; - - let x = stack[0]; - - // Update second stack entry in place: - let stack[0] = Interpreter.multiply(x, y); - } - else { - // 17 - // quotient :: not implemented - - do Interpreter.halt(); - } - } - else { // opcode >= 18 - if (opcode < 19) { - // 18 - // getchar :: -- (blocks until a key is pressed) - - // TODO - let tty[0] = opcode; // DEBUG - do Interpreter.halt(); - } - else { - // 19 - // putchar :: not implemented - - do Interpreter.halt(); - } - } - } - else { // opcode >= 20 - if (opcode < 22) { - if (opcode < 21) { - // 20 - // peek :: x -- RAM[x] - - // TODO - let tty[0] = opcode; // DEBUG - do Interpreter.halt(); - } - else { - // 21 - // poke :: x y -- y (and write the value y at RAM[x]) - - // Note: one rib becomes garbage: - // TODO: inline it - // let y = Interpreter.pop(); - let y = stack[0]; - let stack = stack[1]; - - let tmp21 = stack[0]; - let tmp21[0] = y; - - // Update the second stack entry in place - let stack[0] = y; - } - } - else { // opcode >= 22 - if (opcode < 23) { - // 22 - // halt - - do Interpreter.halt(); - } - else { - // 23 - // screenAddr :: x y -- address of character at column x, line y - - do Interpreter.screenAddress(); - } - } - } - } - else { // opcode >= 24 - do Interpreter.halt(); - } - } - return; - } - /** This was lifted verbatim from Math.jack in the solutions for project 12 from the book, except for inlining abs(). @@ -636,40 +704,6 @@ class Interpreter { } } - /* - Cheat: calculate the address of a character in the screen buffer, using - hand-coded "shift" and adds to multiply by 80, and without any allocation. - - Doing its own (interpreter) stack manipulation to keep variables local and avoid arguments - on the (Jack) stack, and doing it by hand so this can be a leaf function. - - TODO: implement function inlining and just use pop/peek/replace - */ - function Array screenAddress() { - var int x, y, acc0, acc1, acc2, acc3, acc4, acc5, acc6, addr; - - // let y = Interpreter.pop(); - let y = stack[0]; - let stack = stack[1]; - // let x = Interpreter.pop(); - let x = stack[0]; // i.e. peek() - - // 80x = 8*(4x + x) = 2(2(2(2(2(2x) + x)))) - let acc0 = y + y; // 2y - let acc1 = acc0 + acc0; // 4y - let acc2 = acc1 + y; // 5y - let acc3 = acc2 + acc2; // 10y - let acc4 = acc3 + acc3; // 20y - let acc5 = acc4 + acc4; // 40y - let acc6 = acc5 + acc5; // 80y - - let addr = 2048 + acc6 + x; - // do Interpreter.push(addr); - let stack[0] = addr; // replace(); - - return; - } - /** Allocate a rib on the heap, filling in the three fields. */ function Rib alloc(int x, int y, int z) { var Rib r; diff --git a/alt/scheme/example/output.scm b/alt/scheme/example/output.scm index aa879a0..11efac0 100644 --- a/alt/scheme/example/output.scm +++ b/alt/scheme/example/output.scm @@ -74,3 +74,4 @@ ;; 2.4M cycles after assigning sources for binary/comp exprs to D ;; 2.1M cycles and 36% of the heap after adding a "screenAddress" primitive ;; 1.8M cycles after inlining calls in "call" sequence (wrangleClosureParams) +;; 1.6M cycles after replacing the big nested dispatch with a table of function pointers From 8e43b4f6fa6fcc4835fc9cc084fc0e2672be72ea Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Wed, 28 Feb 2024 13:01:46 -0500 Subject: [PATCH 180/221] Switch to screenAddr primitive; cleanup --- alt/scheme/repl.py | 85 ++++++++++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 33 deletions(-) diff --git a/alt/scheme/repl.py b/alt/scheme/repl.py index 998fc75..d8acd23 100755 --- a/alt/scheme/repl.py +++ b/alt/scheme/repl.py @@ -12,51 +12,70 @@ def main(): with open("alt/scheme/ribbit/min.scm") as f: min_library_src_lines = f.readlines() - # TODO: need to reference the library functions we want to be able available program = "".join(min_library_src_lines) + """ - (define poke (rib 21 0 1)) +(define poke (rib 21 0 1)) - (define screen 2048) - (define (drawchar x y c) (poke (+ screen (+ x (* 80 y))) c)) +;; (define screen 2048) +;; (define (drawchar x y c) (poke (+ screen (+ x (* 80 y))) c)) +;; TODO: pre-compute start address of each row? Is that actually faster than multiply, which is - (define cursorx 0) - (define cursory 0) - (define (putchar c) +(define screenAddr (rib 23 0 1)) +(define (drawchar x y c) (poke (screenAddr x y) c)) + +(define cursorx 0) +(define cursory 0) +(define (putchar c) + (if (eqv? c 10) + (begin + (set! cursorx 0) + (set! cursory (+ 1 cursory))) (begin (drawchar cursorx cursory c) - (set! cursorx (+ 1 cursorx)))) - - (repl) - """ + (set! cursorx (+ 1 cursorx))))) + +;; The getchar primitive just blocks and then returns a non-zero char. The repl seems to +;; expect getchar to handle echo, etc. +;; TODO: use a (let ...) here to hide this definition +(define getchar-primitive (rib 18 0 1)) +(define (getchar) + (let ((c (getchar-primitive))) + (if (eqv? c 128) ;; newline, according to the strange key mapping + (begin + (set! cursorx 0) + (set! cursory (+ 1 cursory)) + 10) ;; regular ASCII newline + (begin + (putchar c) + c)))) + +;; TEMP: this should be good enough to convert single-digit numbers to strings for display +(define (quotient x y) 0) + +;; TEMP: until I get exports to work +(define + (rib 14 0 1)) + +(repl) + +;; Exported symbols. + +(export + +* ++ +- +< += +cons +) +""" # Note: actually running the compiler in the Ribbit Python interpreter is pretty slow. # Probably want to cache the encoded result somewhere (or just go back to hard-coding it here.) print("Compiling...") - rvm.run(program, simulator="codegen", print_asm=False, trace_level=rvm.TRACE_COARSE) - - -def temp(): - """Simpler examples for now.""" - -# pgm = """ -# ;; In lieu of pre-defining additional primitives and re-building rsc, -# ;; with the same result: -# (define poke (##rib 21 0 1)) - -# (poke 16384 21845) -# """ - -# pgm = """ -# (+ 1 2) -# """ - - pgm = "42" - - rvm.run(program=pgm) + rvm.run(program, interpreter="assembly", simulator="compiled", print_asm=False, trace_level=rvm.TRACE_COARSE) if __name__ == "__main__": main() - # temp() From ac7a4ef8f61140769b37f83476c1737a1420ecce Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Wed, 28 Feb 2024 13:07:08 -0500 Subject: [PATCH 181/221] =?UTF-8?q?Move=20initialization=20into=20Jack,=20?= =?UTF-8?q?using=20Jack.symbol();=20improved=20=E2=80=94print?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Much nicer printing of generated assembly: Addresses for every instruction. Labels used more hlepfully. No separate printing of every label’s address. --- alt/scheme/Interpreter.jack | 41 ++++++--------- alt/scheme/rvm.py | 99 +++++++++++++++---------------------- nand/translate.py | 14 ++++++ 3 files changed, 69 insertions(+), 85 deletions(-) diff --git a/alt/scheme/Interpreter.jack b/alt/scheme/Interpreter.jack index 5422d1a..4e8032f 100644 --- a/alt/scheme/Interpreter.jack +++ b/alt/scheme/Interpreter.jack @@ -11,19 +11,6 @@ class Interpreter { // Pointers to the functions that handle each primitive: static Array handlers; - // Pre-allocated "proc" containing the "rib" primitive, which will be used to construct every - // other primitive. - static Rib ribRib; - - // Pre-allocated special values: - static Rib ribFalse; - static Rib ribTrue; - static Rib ribNil; - - // Used only during intialization: - static Array symbolNameTableStart; - static Array symbolNameTableEnd; - static Array tty; // DEBUG /* @@ -44,7 +31,7 @@ class Interpreter { var Rib newCont; // saved to update after constructing stack var Rib symbolToUpdate; // need the location to update after .pop() - // Initialize some more state: + // Initialize state: let tty = 4095; // DEBUG @@ -55,7 +42,11 @@ class Interpreter { do Interpreter.initPrimitiveVectors(); + // Bottom of the stack: "primordial continuation" in ROM + let stack = Jack.symbol("rib_outer_cont"); + // Skip the bogus "main" instr: + let pc = Jack.symbol("main"); let pc = pc[2]; while (1) { @@ -163,7 +154,7 @@ class Interpreter { if (opcode = 4) { // if - if (Interpreter.pop() = ribFalse) { + if (Interpreter.pop() = Jack.symbol("rib_false")) { let pc = pc[2]; } else { @@ -194,9 +185,9 @@ class Interpreter { var Rib symbol; var Rib entry; - let entry = ribNil; - let ptr = symbolNameTableStart; - while (ptr < symbolNameTableEnd) { + let entry = Jack.symbol("rib_nil"); + let ptr = Jack.symbol("symbol_names_start"); + while (ptr < Jack.symbol("symbol_names_end")) { // let tty[0] = ptr; // DEBUG let symbol = Interpreter.alloc(ptr[1], ptr[0], 2); // symbol type let entry = Interpreter.alloc(symbol, entry, 0); // pair type @@ -209,7 +200,7 @@ class Interpreter { function void initPrimitiveVectors() { // Just below the screen buffer - let handlers = 2048 - 32; + let handlers = Jack.symbol("SCREEN") - 32; let handlers[ 0] = Jack.symbol("interpreter.handleRib"); let handlers[ 1] = Jack.symbol("interpreter.handleId"); @@ -314,10 +305,10 @@ class Interpreter { let tmp = stack[0]; if (Rib.isRib(tmp)) { - let stack[0] = ribTrue; + let stack[0] = Jack.symbol("rib_true"); } else { - let stack[0] = ribFalse; + let stack[0] = Jack.symbol("rib_false"); } return; @@ -420,10 +411,10 @@ class Interpreter { let x = stack[0]; if (x = y) { - let stack[0] = ribTrue; + let stack[0] = Jack.symbol("rib_true"); } else { - let stack[0] = ribFalse; + let stack[0] = Jack.symbol("rib_false"); } return; @@ -442,10 +433,10 @@ class Interpreter { let x = stack[0]; // Update second stack entry in place: if (x < y) { - let stack[0] = ribTrue; + let stack[0] = Jack.symbol("rib_true"); } else { - let stack[0] = ribFalse; + let stack[0] = Jack.symbol("rib_false"); } return; diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 053f2b1..bce63e8 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -51,9 +51,8 @@ def assemble(encoded, interpreter, print_asm): decode(encoded, asm) - # TODO: print with addresses if print_asm: - for l in asm.lines: print(l) + for l in asm.pretty(big.ROM_BASE): print(l) print() if interpreter == "assembly": @@ -65,13 +64,19 @@ def assemble(encoded, interpreter, print_asm): halt_loop_addr = symbols.get("halt_loop") elif interpreter == "jack": from nand.solutions import solved_06 - instrs, symbols, statics = big.assemble(asm.lines, builtins=solved_06.BUILTIN_SYMBOLS) + builtins = { + **solved_06.BUILTIN_SYMBOLS, + "SCREEN": big.SCREEN_BASE, + } + instrs, symbols, statics = big.assemble(asm.lines, builtins=builtins) stack_loc = statics["interpreter.static_stack"] pc_loc = statics["interpreter.static_pc"] next_rib_loc = statics["interpreter.static_nextRib"] interp_loop_addr = first_loop_in_function(symbols, "Interpreter", "main") halt_loop_addr = first_loop_in_function(symbols, "Interpreter", "halt") - print(interp_loop_addr, halt_loop_addr) + unexpected_statics = [s for s in statics if (".static_" not in s)] + if unexpected_statics != []: + raise Exception(f"unexpected statics: {unexpected_statics}") assert symbols["start"] == big.ROM_BASE @@ -79,13 +84,13 @@ def assemble(encoded, interpreter, print_asm): def show_map(label, m): print("\n".join( [ f"{label}:" ] + - [ f" {addr:5d}: {name}" + [ f"{addr:5d}: {name}" for name, addr in sorted(m.items(), key=lambda t: t[1]) ] + [""] )) - show_map("Symbols", symbols) + # show_map("Symbols", symbols) if statics != {}: show_map("Statics", statics) @@ -178,20 +183,6 @@ def decode(input, asm): See https://github.com/udem-dlteam/ribbit/blob/dev/src/host/py/rvm.py#L126 """ - # base_addr = len(big.assemble(asm.lines)[0]) - - # next_rib = 0 - - # def emit_rib(x, y, z): - # nonlocal next_rib - # name = f"rib{next_rib}" - # asm.label(name) - # asm.instr(x) - # asm.instr(y) - # asm.instr(z) - # next_rib += 1 - # return f"@{name}" - asm.comment("=== Data ===") # Exactly one primitive proc rib is pre-defined: `rib` @@ -241,19 +232,14 @@ def emit_rib(lbl, x, y, z, comment=None): asm.instr(y) asm.instr(z) - def emit_pair(lbl, car, cdr): emit_rib(lbl, car, cdr, "#0") - def emit_proc(lbl, code, env): emit_rib(lbl, code, env, "#1") - def emit_symbol(lbl, value, name): emit_rib(lbl, value, name, "#2") - def emit_string(lbl, chars, count: int): emit_rib(lbl, chars, f"#{count}", "#3") - def emit_vector(lbl, elems, count: int): emit_rib(lbl, elems, f"#{count}", "#4") - - + def emit_pair(lbl, car, cdr, comment): emit_rib(lbl, car, cdr, "#0", comment) + def emit_string(lbl, chars, count: int, comment): emit_rib(lbl, chars, f"#{count}", "#3", comment) # Strings for the symbol table, as constant ribs directly in the ROM: # One empty string that can be shared: - emit_string("rib_string_empty", "@rib_nil", 0) + emit_string("rib_string_empty", "@rib_nil", 0, '""') # First byte(s): number of symbols without names n = get_int(0) @@ -270,9 +256,8 @@ def emit_vector(lbl, elems, count: int): emit_rib(lbl, elems, f"#{count}", "#4" if acc_str == "": lbl = "rib_string_empty" else: - asm.comment(f'"{acc_str}"') lbl = f"rib_string_{idx}" - emit_string(lbl, accum, len(acc_str)) + emit_string(lbl, accum, len(acc_str), f'"{acc_str}"') idx += 1 sym_names.insert(0, (lbl, acc_str)) @@ -282,9 +267,8 @@ def emit_vector(lbl, elems, count: int): emit_rib(lbl, elems, f"#{count}", "#4" if c == ord(";"): break else: - asm.comment(f"'{chr(c)}'") lbl = asm.next_label("char") - emit_pair(lbl, f"#{hex(c)}", accum) + emit_pair(lbl, f"#{hex(c)}", accum, f"'{chr(c)}'") accum = f"@{lbl}" acc_str = chr(c) + acc_str @@ -336,24 +320,30 @@ def symbol_ref(idx): The table is written from the end, and each entry is made of of two ribs, the `symbol` and a `pair`. """ - asm.comment(f'symbol_ref({idx}); "{sym_names[idx][1]}"') - return f"#{big.HEAP_BASE + 6*(len(sym_names) - idx - 1)}" + name = sym_names[idx][1] + description = f'"{name}"({idx})' + return f"#{big.HEAP_BASE + 6*(len(sym_names) - idx - 1)}", description - def emit_instr(op, arg, next): + def emit_instr(op, arg, next, sym): lbl = asm.next_label("instr") asm.label(lbl) + if sym is not None: + target = sym + else: + target = arg + if op == 0 and next == "#0": - asm.comment(f"jump {arg} ") + asm.comment(f"jump {target} ") elif op == 0: - asm.comment(f"call {arg} -> {next}") + asm.comment(f"call {target} -> {next}") elif op == 1: - asm.comment(f"set {arg} -> {next}") + asm.comment(f"set {target} -> {next}") elif op == 2: - asm.comment(f"get {arg} -> {next}") + asm.comment(f"get {target} -> {next}") elif op == 3: - asm.comment(f"const {arg} -> {next}") + asm.comment(f"const {target} -> {next}") elif op == 4: asm.comment(f"if -> {arg} else {next}") else: @@ -381,6 +371,8 @@ def emit_instr(op, arg, next): d = 0 op = 0 + sym = None + while True: d = [20, 30, 0, 10, 11, 4][op] if n <= 2+d: break @@ -397,9 +389,9 @@ def emit_instr(op, arg, next): n = f"#{get_int(0)}" elif n >= d: idx = get_int(n-d-1) - n = symbol_ref(idx) + n, sym = symbol_ref(idx) elif op < 3: - n = symbol_ref(n) + n, sym = symbol_ref(n) if op > 4: # This is either a lambda, or the outer proc that wraps the whole program. @@ -429,9 +421,11 @@ def emit_instr(op, arg, next): if isinstance(n, int): n = f"#{n}" - instr_lbl = emit_instr(op-1, n, pop()) + instr_lbl = emit_instr(op-1, n, pop(), sym) push(f"@{instr_lbl}") + sym = None + # This will be the body of the outer proc, so just "jump" straight to it: start_instr = n # Note: emit_instr would want to choose the label... @@ -1625,7 +1619,7 @@ def return_from_primitive(): asm.label("primitive_*") - asm.comment("primitive 16; - :: x y -- x * y") + asm.comment("primitive 16; * :: x y -- x * y") asm.comment("TEMP_0 = pop() = y") pop("TEMP_0") asm.comment("TEMP_1 = SP.x = x") @@ -1781,22 +1775,6 @@ def init_global(comment, addr, value): # THIS and THAT definitely don't need to be set up before the first function call asm.blank() - asm.comment("Initialize interpreter state that needs to point to data in ROM") - - init_global("Interpreter stack", "interpreter.static_stack", "rib_outer_cont") - init_global("Interpreter pc", "interpreter.static_pc", "main") - - init_global("Primitive proc: 'rib'", "interpreter.static_ribRib", "rib_rib") - - init_global("Constant: #f", "interpreter.static_ribFalse", "rib_false") - init_global("Constant: #t", "interpreter.static_ribTrue", "rib_true") - init_global("Constant: '()", "interpreter.static_ribNil", "rib_nil") - - init_global("Symbol Names (start)", "interpreter.static_symbolNameTableStart", "symbol_names_start") - init_global("Symbol Names (end)", "interpreter.static_symbolNameTableEnd", "symbol_names_end") - - asm.blank() - translator = reg.Translator(asm) def load_class(path): @@ -1816,6 +1794,7 @@ def load_class(path): return asm + def first_loop_in_function(symbols, class_name, function_name): """Address of the first instruction labeled "loop_... found (probably) within the given function.""" diff --git a/nand/translate.py b/nand/translate.py index 1734f5c..d0f78ad 100644 --- a/nand/translate.py +++ b/nand/translate.py @@ -108,6 +108,20 @@ def find_function(self, class_name, function_name): return start, ends + def pretty(self, start_addr=0): + """Lines of code, including the location in ROM of each instruction, as a geneerator.""" + loc = start_addr + for l in self.lines: + raw = l.strip() + if raw == "" or raw.startswith("//"): + yield f" {l}" + elif raw.startswith("("): + yield f" {l}" + else: + yield f"{loc:5d}: {l}" + loc += 1 + + # TODO: find a better home for this (nand.runtime? .execute?, .debug?) def run(self, assembler, computer, stop_cycles=None, debug=False, tty=None): """Step through the execution of the generated program, using the provided assembler and From f23aab84216211d0ccb50fed162498d2c7739927 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Wed, 28 Feb 2024 13:49:20 -0500 Subject: [PATCH 182/221] =?UTF-8?q?Implement=20=E2=80=9Cgetchar=E2=80=9D?= =?UTF-8?q?=20primitive=20in=20Jack;=20echo.scm=20example?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- alt/scheme/Interpreter.jack | 23 ++++++++++++++----- alt/scheme/example/echo.scm | 8 +++++++ alt/scheme/io.scm | 44 +++++++++++++++++++++++++++++++++++++ alt/scheme/rvm.py | 2 +- 4 files changed, 71 insertions(+), 6 deletions(-) create mode 100644 alt/scheme/example/echo.scm create mode 100644 alt/scheme/io.scm diff --git a/alt/scheme/Interpreter.jack b/alt/scheme/Interpreter.jack index 4e8032f..8b34b7b 100644 --- a/alt/scheme/Interpreter.jack +++ b/alt/scheme/Interpreter.jack @@ -494,13 +494,26 @@ class Interpreter { return; } - /* getchar :: -- (blocks until a key is pressed) */ + /* getchar :: -- (blocks until a key is pressed) + + Note: this will only catch keypresses that occur after the instruction is executed. For + a responsive shell, the check will have to incorporated into the interpreter loop. + It might even need to be be checked more often than once per instruction, since instructions + can take as long as hundreds of cycles. + */ function void handleGetchar() { - var int x, y, z; - var Rib tmp; + var Array keyboard; + var int c; - // TODO - do Interpreter.halt(); + let keyboard = Jack.symbol("KEYBOARD"); + let c = 0; + while (c = 0) { + let c = keyboard[0]; + } + + do Interpreter.push(c); + + return; } /* peek :: x -- RAM[x] */ diff --git a/alt/scheme/example/echo.scm b/alt/scheme/example/echo.scm new file mode 100644 index 0000000..c71b42b --- /dev/null +++ b/alt/scheme/example/echo.scm @@ -0,0 +1,8 @@ +;; Note: io.scm should be loaded first + +(define (echo) + (let ((c (getchar))) + ;;(putchar c) + (echo))) + +(echo) \ No newline at end of file diff --git a/alt/scheme/io.scm b/alt/scheme/io.scm new file mode 100644 index 0000000..3b04792 --- /dev/null +++ b/alt/scheme/io.scm @@ -0,0 +1,44 @@ +;; I/O "primitives" for the REPL and any other terminal-style programs. +;; +;; In this implementation, getchar and putchar aren't provided directly by the VM, because there's +;; no OS-level support for getting characters from the keyboard and onto the screen. Instead, the +;; VM provides a lower-level "getchar" (which is a blocking read of the location in memory where +;; the keyboard is mapped), generic "peek" and "poke" primitives which can read and write the +;; screen buffer, and (for peformance) a "screenAddr" primitive which calculates addresses in the +;; screen buffer memory faster than interpreted schems could and without allocation. + + +(define poke (rib 21 0 1)) + +;; (define screen 2048) +;; (define (drawchar x y c) (poke (+ screen (+ x (* 80 y))) c)) + +(define screenAddr (rib 23 0 1)) +(define (drawchar x y c) (poke (screenAddr x y) c)) + +(define cursorx 0) +(define cursory 0) +(define (putchar c) + (if (eqv? c 10) + (begin + (set! cursorx 0) + (set! cursory (+ 1 cursory))) + (begin + (drawchar cursorx cursory c) + (set! cursorx (+ 1 cursorx))))) + +;; The getchar primitive just blocks and then returns a non-zero char. The repl seems to +;; expect getchar to handle echo, etc. +;; TODO: use a (let ...) here to hide this definition +(define getchar-primitive (rib 18 0 1)) +(define (getchar) + (let ((c (getchar-primitive))) + (if (eqv? c 128) ;; newline, according to the strange key mapping + (begin + (set! cursorx 0) + (set! cursory (+ 1 cursory)) + 10) ;; regular ASCII newline + (begin + (putchar c) + c)))) + diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index bce63e8..1956c17 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -66,7 +66,7 @@ def assemble(encoded, interpreter, print_asm): from nand.solutions import solved_06 builtins = { **solved_06.BUILTIN_SYMBOLS, - "SCREEN": big.SCREEN_BASE, + **big.BUILTIN_SYMBOLS, } instrs, symbols, statics = big.assemble(asm.lines, builtins=builtins) stack_loc = statics["interpreter.static_stack"] From 7c23bd5e6397b8e597d1000e12c0aa45288f380a Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Thu, 29 Feb 2024 09:32:38 -0500 Subject: [PATCH 183/221] Massive simulation speedup (by throttling screen updates) --- alt/big.py | 64 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 41 insertions(+), 23 deletions(-) diff --git a/alt/big.py b/alt/big.py index 17da095..6019c98 100755 --- a/alt/big.py +++ b/alt/big.py @@ -313,6 +313,13 @@ def assemble(f, min_static=16, max_static=1023, builtins=BUILTIN_SYMBOLS): import time +EVENT_INTERVAL = 1/10 +DISPLAY_INTERVAL = 1/20 # Note: screen update is pretty slow at this point, so no point in trying for a higher frame rate. +CYCLE_INTERVAL = 1/1.0 # How often to update the cycle and frame counters; a bit longer so they doesn't bounce around too much + +CYCLES_PER_CALL = 100 # Number of cycles to run in the tight loop (when not tracing) + + def run(program, chip=BigComputer, simulator="codegen", name="Flat!", font="monaco-9", halt_addr=None, trace=None, verbose_tty=True): """Run with keyboard and text-mode graphics.""" @@ -336,39 +343,46 @@ def run(program, chip=BigComputer, simulator="codegen", name="Flat!", font="mona # TODO: use computer.py's "run", for many more features - cycles = 0 + last_cycle_time = last_event_time = last_display_time = last_frame_time = now = time.monotonic() halted = False + + last_cycle_count = cycles = 0 while True: if not halted and computer.pc == halt_addr: halted = True - print(f"\nHalted after {cycles} cycles\n") + print(f"\nHalted after {cycles:,d} cycles\n") if trace is not None: trace(computer, cycles) - if not halted: - if trace is not None: - cycles_per_call = 1 - else: - cycles_per_call = 10 # has to be a factor of CYCLES_PER_UPDATE - computer.ticktock(cycles_per_call) - cycles += cycles_per_call + if halted: + time.sleep(EVENT_INTERVAL) + + elif trace is None: + computer.ticktock(CYCLES_PER_CALL) + cycles += CYCLES_PER_CALL + + else: + computer.ticktock() + cycles += 1 if computer.fetch and trace is not None: trace(computer, cycles) - else: - time.sleep(0.1) + if cycles % CYCLES_PER_CALL == 0 or halted: + now = time.monotonic() - if trace is not None: - CYCLES_PER_UPDATE = 20 # HACK: to ensure we see all the TTY output - else: - CYCLES_PER_UPDATE = 100 + # A few times per second, process events and update the display: + if now >= last_event_time + EVENT_INTERVAL: + last_event_time = now + key = kvm.process_events() + computer.set_keydown(key or 0) + + if now >= last_display_time + DISPLAY_INTERVAL: + last_display_time = now + kvm.update_display(lambda x: computer.peek(SCREEN_BASE + x)) - if cycles % CYCLES_PER_UPDATE == 0 or halted: - key = kvm.process_events() - computer.set_keydown(key or 0) tty_char = computer.get_tty() if tty_char: @@ -380,14 +394,18 @@ def run(program, chip=BigComputer, simulator="codegen", name="Flat!", font="mona else: print(f"TTY: ({tty_char:6d}; 0x{unsigned(tty_char):04x})") - kvm.update_display(lambda x: computer.peek(SCREEN_BASE + x)) - msgs = [ - f"{cycles/1000:0.1f}k cycles", - f"@{computer.pc}", - ] + msgs = [] + msgs.append(f"{cycles/1000:0.1f}k cycles") + cps = (cycles - last_cycle_count)/(now - last_cycle_time) + msgs.append(f"{cps/1000:0,.1f}k/s") + msgs.append(f"@{computer.pc}") + # TODO: let the caller add meters (e.g. Ribbit heap consumption) pygame.display.set_caption(f"{name}: {'; '.join(msgs)}") + last_cycle_time = now + last_cycle_count = cycles + class TextKVM(computer.KVM): """Keyboard and display, displaying characters using a set of baked-in glyphs. From 5b3527324fe5be6d7882e16bf27039c66c93a653 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Thu, 29 Feb 2024 09:33:04 -0500 Subject: [PATCH 184/221] =?UTF-8?q?Implement=20=E2=80=9Carg2=E2=80=9D=20pr?= =?UTF-8?q?imitive?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- alt/scheme/Interpreter.jack | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/alt/scheme/Interpreter.jack b/alt/scheme/Interpreter.jack index 8b34b7b..7bb4534 100644 --- a/alt/scheme/Interpreter.jack +++ b/alt/scheme/Interpreter.jack @@ -279,8 +279,14 @@ class Interpreter { /* arg2 :: x y -- y */ function void handleArg2() { - // TODO - do Interpreter.halt(); + var int y; + + // y = pop() + let y = stack[0]; + let stack = stack[1]; + + // replace(y) + let stack[0] = y; return; } From d938b6de9184247042814c5a19b9fd71fa9b2662 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Thu, 29 Feb 2024 09:33:58 -0500 Subject: [PATCH 185/221] =?UTF-8?q?Use=20io.scm;=20commit=20ribbit?= =?UTF-8?q?=E2=80=99s=20min=20library?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- alt/scheme/repl.py | 53 +-- alt/scheme/ribbit/min.scm | 962 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 975 insertions(+), 40 deletions(-) create mode 100644 alt/scheme/ribbit/min.scm diff --git a/alt/scheme/repl.py b/alt/scheme/repl.py index d8acd23..c47155f 100755 --- a/alt/scheme/repl.py +++ b/alt/scheme/repl.py @@ -9,45 +9,15 @@ from alt.scheme import rvm def main(): - with open("alt/scheme/ribbit/min.scm") as f: - min_library_src_lines = f.readlines() - - program = "".join(min_library_src_lines) + """ - -(define poke (rib 21 0 1)) - -;; (define screen 2048) -;; (define (drawchar x y c) (poke (+ screen (+ x (* 80 y))) c)) -;; TODO: pre-compute start address of each row? Is that actually faster than multiply, which is - -(define screenAddr (rib 23 0 1)) -(define (drawchar x y c) (poke (screenAddr x y) c)) - -(define cursorx 0) -(define cursory 0) -(define (putchar c) - (if (eqv? c 10) - (begin - (set! cursorx 0) - (set! cursory (+ 1 cursory))) - (begin - (drawchar cursorx cursory c) - (set! cursorx (+ 1 cursorx))))) - -;; The getchar primitive just blocks and then returns a non-zero char. The repl seems to -;; expect getchar to handle echo, etc. -;; TODO: use a (let ...) here to hide this definition -(define getchar-primitive (rib 18 0 1)) -(define (getchar) - (let ((c (getchar-primitive))) - (if (eqv? c 128) ;; newline, according to the strange key mapping - (begin - (set! cursorx 0) - (set! cursory (+ 1 cursory)) - 10) ;; regular ASCII newline - (begin - (putchar c) - c)))) + def load(path): + with open(f"alt/scheme/{path}") as f: + return f.readlines() + + + min_library_src_lines = load("ribbit/min.scm") + io_src_lines = load("io.scm") + + program = "".join(min_library_src_lines + io_src_lines) + """ ;; TEMP: this should be good enough to convert single-digit numbers to strings for display (define (quotient x y) 0) @@ -74,7 +44,10 @@ def main(): # Probably want to cache the encoded result somewhere (or just go back to hard-coding it here.) print("Compiling...") - rvm.run(program, interpreter="assembly", simulator="compiled", print_asm=False, trace_level=rvm.TRACE_COARSE) + rvm.run(program, interpreter="jack", simulator="compiled", + print_asm=True, + trace_level=rvm.TRACE_NONE) + # trace_level=rvm.TRACE_COARSE) if __name__ == "__main__": diff --git a/alt/scheme/ribbit/min.scm b/alt/scheme/ribbit/min.scm new file mode 100644 index 0000000..9b53bf5 --- /dev/null +++ b/alt/scheme/ribbit/min.scm @@ -0,0 +1,962 @@ +;; Ribbit Scheme runtime library. + +;; This is the "min" version with only the core R4RS predefined procedures. + +;;;---------------------------------------------------------------------------- + +;; Implementation of Ribbit Scheme types using the RVM operations. + +(define pair-type 0) +(define procedure-type 1) +(define symbol-type 2) +(define string-type 3) +(define vector-type 4) +(define singleton-type 5) + +(define (instance? type) (lambda (o) (and (rib? o) (eqv? (field2 o) type)))) + +;;;---------------------------------------------------------------------------- + +;; Booleans (R4RS section 6.1). + +(define (not x) (eqv? x #f)) + +;;(define (boolean? obj) (or (eqv? obj #t) (not obj))) + +;;;---------------------------------------------------------------------------- + +;; Equivalence predicates (R4RS section 6.2). + +;;(define eq? eqv?) + +(define (equal? x y) + (or (eqv? x y) + (and (rib? x) + (if (eqv? (field2 x) singleton-type) + #f + (and (rib? y) + (equal? (field2 x) (field2 y)) + (equal? (field1 x) (field1 y)) + (equal? (field0 x) (field0 y))))))) + +;;;---------------------------------------------------------------------------- + +;; Pairs and lists (R4RS section 6.3). + +(define pair? (instance? pair-type)) +(define (cons car cdr) (rib car cdr pair-type)) +(define car field0) +(define cdr field1) +(define set-car! field0-set!) +(define set-cdr! field1-set!) + +(define (cadr pair) (field0 (field1 pair))) +(define (cddr pair) (field1 (field1 pair))) +(define (caddr pair) (cadr (field1 pair))) +(define (cadddr pair) (caddr (field1 pair))) + +;;(define (caar pair) (field0 (field0 pair))) +;;(define (cadr pair) (field0 (field1 pair))) +;;(define (cdar pair) (field1 (field0 pair))) +;;(define (cddr pair) (field1 (field1 pair))) + +;;(define (caaar pair) (caar (field0 pair))) +;;(define (caadr pair) (caar (field1 pair))) +;;(define (cadar pair) (cadr (field0 pair))) +;;(define (caddr pair) (cadr (field1 pair))) +;;(define (cdaar pair) (cdar (field0 pair))) +;;(define (cdadr pair) (cdar (field1 pair))) +;;(define (cddar pair) (cddr (field0 pair))) +;;(define (cdddr pair) (cddr (field1 pair))) + +;;(define (caaaar pair) (caaar (field0 pair))) +;;(define (caaadr pair) (caaar (field1 pair))) +;;(define (caadar pair) (caadr (field0 pair))) +;;(define (caaddr pair) (caadr (field1 pair))) +;;(define (cadaar pair) (cadar (field0 pair))) +;;(define (cadadr pair) (cadar (field1 pair))) +;;(define (caddar pair) (caddr (field0 pair))) +;;(define (cadddr pair) (caddr (field1 pair))) +;;(define (cdaaar pair) (cdaar (field0 pair))) +;;(define (cdaadr pair) (cdaar (field1 pair))) +;;(define (cdadar pair) (cdadr (field0 pair))) +;;(define (cdaddr pair) (cdadr (field1 pair))) +;;(define (cddaar pair) (cddar (field0 pair))) +;;(define (cddadr pair) (cddar (field1 pair))) +;;(define (cdddar pair) (cdddr (field0 pair))) +;;(define (cddddr pair) (cdddr (field1 pair))) + +(define (null? obj) (eqv? obj '())) + +;;(define (list? obj) +;; (list?-aux obj obj)) + +;;(define (list?-aux fast slow) +;; (if (pair? fast) +;; (let ((fast (cdr fast))) +;; (cond ((eq? fast slow) +;; #f) +;; ((pair? fast) +;; (list?-aux (cdr fast) (cdr slow))) +;; (else +;; (null? fast)))) +;; (null? fast))) + +;;(define (list . args) args) + +(define (length lst) + (if (pair? lst) + (+ 1 (length (cdr lst))) + 0)) + +;;(define (append lst1 lst2) +;; (if (pair? lst1) +;; (cons (car lst1) (append (cdr lst1) lst2)) +;; lst2)) + +;;(define (reverse lst) +;; (reverse-aux lst '())) + +;;(define (reverse-aux lst result) +;; (if (pair? lst) +;; (reverse-aux (cdr lst) (cons (car lst) result)) +;; result)) + +(define (list-ref lst i) + (car (list-tail lst i))) + +(define (list-set! lst i x) + (set-car! (list-tail lst i) x)) + +(define (list-tail lst i) + (if (< 0 i) + (list-tail (cdr lst) (- i 1)) + lst)) + +;;(define (memv x lst) +;; (if (pair? lst) +;; (if (eqv? x (car lst)) +;; lst +;; (memv x (cdr lst))) +;; #f)) + +;;(define memq memv) + +;;(define (member x lst) +;; (if (pair? lst) +;; (if (equal? x (car lst)) +;; lst +;; (member x (cdr lst))) +;; #f)) + +;;(define (assv x lst) +;; (if (pair? lst) +;; (let ((couple (car lst))) +;; (if (eqv? x (car couple)) +;; couple +;; (assv x (cdr lst)))) +;; #f)) + +;;(define assq assv) + +;;(define (assoc x lst) +;; (if (pair? lst) +;; (let ((couple (car lst))) +;; (if (equal? x (car couple)) +;; couple +;; (assoc x (cdr lst)))) +;; #f)) + +(define (make-list k fill) + (make-list-aux k fill '())) + +(define (make-list-aux k fill lst) + (if (< 0 k) + (make-list-aux (- k 1) fill (cons fill lst)) + lst)) + +;;;---------------------------------------------------------------------------- + +;; Symbols (R4RS section 6.4). + +(define symbol? (instance? symbol-type)) +(define (string->uninterned-symbol str) (rib #f str symbol-type)) +(define symbol->string field1) +(define global-var-ref field0) +(define global-var-set! field0-set!) + +;; Symbol table. + +(define (string->symbol str) + (string->symbol-aux str symtbl)) + +(define (string->symbol-aux str syms) + (if (pair? syms) + (let ((sym (field0 syms))) + (if (equal? (field1 sym) str) + sym + (string->symbol-aux str (field1 syms)))) + (let ((sym (string->uninterned-symbol str))) + (set! symtbl (cons sym symtbl)) + sym))) + +(define symtbl (field1 rib)) ;; get symbol table + +(field1-set! rib 0) ;; release symbol table if not otherwise needed + +;;;---------------------------------------------------------------------------- + +;; Numbers (R4RS section 6.5). + +;;(define (integer? obj) (not (rib? obj))) + +;;(define rational? integer?) +;;(define real? rational?) +;;(define complex? real?) +;;(define number? complex?) + +;;(define (exact? obj) #t) +;;(define (inexact? obj) #f) + +(define = eqv?) +;;(define (> x y) (< y x)) +;;(define (<= x y) (not (< y x))) +;;(define (>= x y) (not (< x y))) + +;;(define (zero? x) (eqv? x 0)) +;;(define (positive? x) (< 0 x)) +;;(define (negative? x) (< x 0)) +;;(define (even? x) (eqv? x (* 2 (quotient x 2)))) +;;(define (odd? x) (not (even? x))) + +;;(define (max x y) (if (< x y) y x)) +;;(define (min x y) (if (< x y) x y)) + +;;(define (abs x) (if (< x 0) (- 0 x) x)) + +;;(define (remainder x y) +;; (- x (* y (quotient x y)))) + +;;(define (modulo x y) +;; (let ((q (quotient x y))) +;; (let ((r (- x (* y q)))) +;; (if (eqv? r 0) +;; 0 +;; (if (eqv? (< x 0) (< y 0)) +;; r +;; (+ r y)))))) + +;;(define (gcd x y) +;; (let ((ax (abs x))) +;; (let ((ay (abs y))) +;; (if (< ax ay) +;; (gcd-aux ax ay) +;; (gcd-aux ay ax))))) + +;;(define (gcd-aux x y) +;; (if (eqv? x 0) +;; y +;; (gcd-aux (remainder y x) x))) + +;;(define (lcm x y) +;; (if (eqv? y 0) +;; 0 +;; (let ((ax (abs x))) +;; (let ((ay (abs y))) +;; (* (quotient ax (gcd ax ay)) ay))))) + +;;(define numerator id) +;;(define (denominator x) 1) + +;;(define floor id) +;;(define ceiling id) +;;(define truncate id) +;;(define round id) + +;;(define (rationalize x y) ...) +;;(define (exp x) ...) +;;(define (log x) ...) +;;(define (sin x) ...) +;;(define (cos x) ...) +;;(define (tan x) ...) +;;(define (asin x) ...) +;;(define (acos x) ...) +;;(define (atan y . x) ...) + +;;(define (sqrt x) ...) + +;;(define (expt x y) +;; (if (eqv? y 0) +;; 1 +;; (let ((t (expt (* x x) (quotient y 2)))) +;; (if (odd? y) +;; (* x t) +;; t)))) + +;;(define (make-rectangular x y) ...) +;;(define (make-polar x y) ...) +;;(define (real-part x) ...) +;;(define (imag-part x) ...) +;;(define (magnitude x) ...) +;;(define (angle x) ...) + +;;(define (exact->inexact x) ...) +;;(define (inexact->exact x) ...) + +;; Integer to string conversion. + +(define (number->string x) + (list->string + (if (< x 0) + (cons 45 (number->string-aux (- 0 x) '())) + (number->string-aux x '())))) + +(define (number->string-aux x tail) + (let ((q (quotient x 10))) + (let ((d (+ 48 (- x (* q 10))))) + (let ((t (cons d tail))) + (if (< 0 q) + (number->string-aux q t) + t))))) + +;; String to integer conversion. + +(define (string->number str) + (let ((lst (string->list str))) + (if (null? lst) + #f + (if (eqv? (car lst) 45) + (string->number-aux (cdr lst)) + (let ((n (string->number-aux lst))) + (and n (- 0 n))))))) + +(define (string->number-aux lst) + (if (null? lst) + #f + (string->number-aux2 lst 0))) + +(define (string->number-aux2 lst n) + (if (pair? lst) + (let ((c (car lst))) + (and (< 47 c) + (< c 58) + (string->number-aux2 (cdr lst) (- (* 10 n) (- c 48))))) + n)) + +;;;---------------------------------------------------------------------------- + +;; Characters (R4RS section 6.6). + +;;(define char? integer?) + +(define char=? eqv?) +(define char? >) +(define char<=? <=) +(define char>=? >=) + +;;(define char-ci=? eqv?) +;;(define char-ci? >) +;;(define char-ci<=? <=) +;;(define char-ci>=? >=) + +;;(define (char-alphabetic? c) ...) +;;(define (char-numeric? c) ...) +;;(define (char-whitespace? c) ...) +;;(define (char-upper-case? c) ...) +;;(define (char-lower-case? c) ...) + +(define char->integer id) +(define integer->char id) + +;;(define (char-upcase c) ...) +;;(define (char-downcase c) ...) + +;;;---------------------------------------------------------------------------- + +;; Strings (R4RS section 6.7). + +(define string? (instance? string-type)) +(define (list->string lst) (rib lst (length lst) string-type)) +(define string->list field0) +(define string-length field1) +(define (string-ref str i) (list-ref (field0 str) i)) +(define (string-set! str i x) (list-set! (field0 str) i x)) + +(define (make-string k) (list->string (make-list k 32))) + +;;(define (string . args) ...) + +;;(define (string=? str1 str2) (eqv? (string-cmp str1 str2) 0)) +;;(define (string? str1 str2) (< 0 (string-cmp str1 str2))) +;;(define (string<=? str1 str2) (not (string>? str1 str2))) +;;(define (string>=? str1 str2) (not (string? string>?) +;;(define string-ci<=? string<=?) +;;(define string-ci>=? string>=?) + +;;(define (string-cmp str1 str2) +;; (string-cmp-aux (string->list str1) (string->list str2))) + +;;(define (string-cmp-aux lst1 lst2) +;; (if (pair? lst1) +;; (if (pair? lst2) +;; (let ((c1 (car lst1))) +;; (let ((c2 (car lst2))) +;; (if (< c1 c2) +;; -1 +;; (if (< c2 c1) +;; 1 +;; (string-cmp-aux (cdr lst1) (cdr lst2)))))) +;; 1) +;; (if (pair? lst2) +;; -1 +;; 0))) + +;;(define (substring str start end) +;; (substring-aux str start end '())) + +;;(define (substring-aux str start end tail) +;; (if (< start end) +;; (let ((i (- end 1))) +;; (substring-aux str start i (cons (string-ref str i) tail))) +;; (list->string tail))) + +;;(define (string-append str1 str2) +;; (list->string (append (string->list str1) +;; (string->list str2)))) + +;;(define (string-copy str) +;; (list->string (append (string->list str) '()))) + +;;(define (string-fill! str fill) +;; (field0-set! str (make-list (field1 str) fill))) + +;;;---------------------------------------------------------------------------- + +;; Vectors (R4RS section 6.8). + +(define vector? (instance? vector-type)) +(define (list->vector lst) (rib lst (length lst) vector-type)) +(define vector->list field0) +(define vector-length field1) +(define (vector-ref vect i) (list-ref (field0 vect) i)) +(define (vector-set! vect i x) (list-set! (field0 vect) i x)) + +(define (make-vector k) (list->vector (make-list k 0))) + +;;(define (vector . args) ...) + +;;(define (vector-fill! vect fill) +;; (field0-set! vect (make-list (field1 vect) fill))) + +;;;---------------------------------------------------------------------------- + +;; Control features (R4RS section 6.9). + +(define procedure? (instance? procedure-type)) +(define (make-procedure code env) (rib code env procedure-type)) +(define procedure-code field0) +(define procedure-env field1) + +;;(define (apply proc . args) ...) + +;;(define (map proc lst) +;; (if (pair? lst) +;; (cons (proc (car lst)) (map proc (cdr lst))) +;; '())) + +;;(define (for-each proc lst) +;; (if (pair? lst) +;; (begin +;; (proc (car lst)) +;; (for-each proc (cdr lst))) +;; #f)) + +;; First-class continuations. + +(define (call/cc receiver) + (let ((c (field1 (field1 (close #f))))) ;; get call/cc continuation rib + (receiver (lambda (r) + (let ((c2 (field1 (field1 (close #f))))) + (field0-set! c2 (field0 c)) ;; set "stack" field + (field2-set! c2 (field2 c)) ;; set "pc" field + r))))) ;; return to continuation + +;;;---------------------------------------------------------------------------- + +;; Input and output (R4RS section 6.10). + +;;(define (call-with-input-file string proc) ...) +;;(define (call-with-output-file string proc) ...) +;;(define (input-port? obj) ...) +;;(define (output-port? obj) ...) +;;(define (current-input-port) ...) +;;(define (current-output-port) ...) +;;(define (with-input-from-file string thunk) ...) +;;(define (with-output-to-file string thunk) ...) +;;(define (open-input-file filename) ...) +;;(define (open-output-file filename) ...) +;;(define (close-input-port port) ...) +;;(define (close-output-port port) ...) +;;(define (char-ready?) ...) +;;(define (load filename) ...) +;;(define (transcript-on filename) ...) +;;(define (transcript-off) ...) + +;; Character I/O (characters are represented with integers). + +(define eof -1) +(define (eof-object? obj) (eqv? obj eof)) + +(define empty -2) +(define buffer empty) + +(define (read-char) + (let ((c buffer)) + (if (eqv? c eof) + c + (read-char-aux + (if (eqv? c empty) + (getchar) + c))))) + +(define (read-char-aux c) + (set! buffer c) + (if (eqv? c eof) + c + (begin + (set! buffer empty) + c))) + +(define (peek-char) + (let ((c (read-char))) + (set! buffer c) + c)) +;; +;; ;;;---------------------------------------------------------------------------- +;; +;; The read procedure. + +(define (read) + (let ((c (peek-char-non-whitespace))) + (cond ((< c 0) + c) + ((eqv? c 40) ;; #\( + (read-char) ;; skip "(" + (read-list)) + ((eqv? c 35) ;; #\# + (read-char) ;; skip "#" + (let ((c (peek-char))) + (cond ((eqv? c 102) ;; #\f + (read-char) ;; skip "f" + #f) + ((eqv? c 116) ;; #\t + (read-char) ;; skip "t" + #t) + (else ;; assume it is #\( + (list->vector (read)))))) + ((eqv? c 39) ;; #\' + (read-char) ;; skip "'" + (cons 'quote (cons (read) '()))) +;; ((eqv? c 34) ;; #\" +;; (read-char) ;; skip """ +;; (list->string (read-chars '()))) + (else + (read-char) ;; skip first char + (let ((s (list->string (cons c (read-symbol))))) + (let ((n (string->number s))) + (or n + (string->symbol s)))))))) + +(define (read-list) + (let ((c (peek-char-non-whitespace))) + (if (eqv? c 41) ;; #\) + (begin + (read-char) ;; skip ")" + '()) + (let ((first (read))) + (cons first (read-list)))))) + +(define (read-symbol) + (let ((c (peek-char))) + (if (or (eqv? c 40) ;; #\( + (eqv? c 41) ;; #\) + (< c 33)) ;; whitespace or eof? + '() + (begin + (read-char) + (cons c (read-symbol)))))) + +;;(define (read-chars lst) +;; (let ((c (read-char))) +;; (cond ((eof-object? c) +;; '()) +;; ((eqv? c 34) ;; #\" +;; (reverse lst)) +;; ((eqv? c 92) ;; #\\ +;; #; ;; no support for \n in strings +;; (read-chars (cons (read-char) lst)) +;; ;#; ;; support for \n in strings +;; (let ((c2 (read-char))) +;; (read-chars (cons (if (eqv? c2 110) 10 c2) lst)))) +;; (else +;; (read-chars (cons c lst)))))) + +(define (peek-char-non-whitespace) + (let ((c (peek-char))) + (if (eof-object? c) ;; eof? + -1 + (if (< 32 c) ;; above #\space ? + (if (eqv? c 59) ;; #\; + (skip-comment) + c) + (begin + (read-char) + (peek-char-non-whitespace)))))) + +(define (skip-comment) + (let ((c (read-char))) + (if (< c 0) ;; eof? + c + (if (eqv? c 10) ;; #\newline + (peek-char-non-whitespace) + (skip-comment))))) + +;; ;;;---------------------------------------------------------------------------- +;; +;; ;; The write procedure. +;; +(define (write o) + (cond ((string? o) + (putchar 34) + (write-chars (string->list o)) + (putchar 34)) + (else + (display o)))) + +(define (display o) + (cond ((not o) + (putchar2 35 102)) ;; #f + ((eqv? o #t) + (putchar2 35 116)) ;; #t + ((null? o) + (putchar2 40 41)) ;; () + ((pair? o) + (putchar 40) ;; #\( + (write (car o)) + (write-list (cdr o)) + (putchar 41)) ;; #\) + ((symbol? o) + (display (symbol->string o))) + ((string? o) + (write-chars (string->list o))) +;; ((vector? o) +;; (putchar 35) ;; #\# +;; (write (vector->list o))) + ((procedure? o) + (putchar2 35 112)) ;; #p + (else + ;; must be a number + (display (number->string o))))) + +(define (write-list lst) + (if (pair? lst) + (begin + (putchar 32) ;; #\space + (if (pair? lst) + (begin + (write (car lst)) + (write-list (cdr lst))) + #f)) ;; writing dotted pairs is not supported + #f)) + +(define (write-chars lst escape?) + (if (pair? lst) + (let ((c (car lst))) + (putchar + (cond ((not escape?) + c) + ;#; ;; support for \n in strings + ((eqv? c 10) ;; #\newline + (putchar 92) + 110) + ((or (eqv? c 34) ;; #\" + (eqv? c 92)) ;; #\\ + (putchar 92) + c) + (else + c))) + (write-chars (cdr lst) escape?)) + #f)) + +(define (write-chars lst) + (if (pair? lst) + (let ((c (car lst))) + (putchar c) + (write-chars (cdr lst))) + #f)) + +(define (write-char c) + (putchar c)) + +(define (newline) + (putchar 10)) + +(define (putchar2 c1 c2) + (putchar c1) + (putchar c2)) + +;;;---------------------------------------------------------------------------- + +;; Compiler from Ribbit Scheme to RVM code. + +(define jump/call-op 0) +(define set-op 1) +(define get-op 2) +(define const-op 3) +(define if-op 4) + +(define (add-nb-args nb tail) + (if ##feature-arity-check + (rib const-op + nb + tail) + tail)) + +(define (comp cte expr cont) + + (cond ((symbol? expr) + (rib get-op (lookup expr cte 0) cont)) + + ((pair? expr) + (let ((first (car expr))) + (cond ((eqv? first 'quote) + (rib const-op (cadr expr) cont)) + + ((or (eqv? first 'set!) (eqv? first 'define)) + (comp cte + (caddr expr) + (gen-assign (lookup (cadr expr) cte 1) + cont))) + + ((eqv? first 'if) + (comp cte + (cadr expr) + (rib if-op + (comp cte (caddr expr) cont) + (comp cte (cadddr expr) cont)))) + + ((eqv? first 'lambda) + (let ((params (cadr expr))) + (rib const-op + (make-procedure + (rib (* (length params) 2) + 0 + ;#; ;; support for single expression in body + (comp (extend params + (cons #f + (cons #f + cte))) + (caddr expr) + tail) +;; #; ;; support for multiple expressions in body +;; (comp-begin (extend params +;; (cons #f +;; (cons #f +;; cte))) +;; (cddr expr) +;; tail) +) + '()) + (if (null? cte) + cont + (add-nb-args + 1 + (gen-call 'close cont)))))) + +;;#; ;; support for begin special form +;; ((eqv? first 'begin) +;; (comp-begin cte (cdr expr) cont)) +;; +;;#; ;; support for single armed let special form +;; ((eqv? first 'let) +;; (let ((binding (car (cadr expr)))) +;; (comp-bind cte +;; (car binding) +;; (cadr binding) +;; ;#; ;; support for single expression in body +;; (caddr expr) +;; #; ;; support for multiple expressions in body +;; (cddr expr) +;; cont))) +;; +;;#; ;; support for and special form +;; ((eqv? first 'and) +;; (comp cte +;; (if (pair? (cdr expr)) +;; (let ((second (cadr expr))) +;; (if (pair? (cddr expr)) +;; (build-if second +;; (cons 'and (cddr expr)) +;; #f) +;; second)) +;; #t) +;; cont)) +;; +;;#; ;; support for or special form +;; ((eqv? first 'or) +;; (comp cte +;; (if (pair? (cdr expr)) +;; (let ((second (cadr expr))) +;; (if (pair? (cddr expr)) +;; (list3 'let +;; (list1 (list2 '_ second)) +;; (build-if '_ +;; '_ +;; (cons 'or (cddr expr)))) +;; second)) +;; #f) +;; cont)) +;; +;;#; ;; support for cond special form +;; ((eqv? first 'cond) +;; (comp cte +;; (if (pair? (cdr expr)) +;; (if (eqv? 'else (car (cadr expr))) +;; (cons 'begin (cdr (cadr expr))) +;; (build-if (car (cadr expr)) +;; (cons 'begin (cdr (cadr expr))) +;; (cons 'cond (cddr expr)))) +;; #f) +;; cont)) + + (else + ;#; ;; support for calls with only variable in operator position + (comp-call cte + (cdr expr) + (length (cdr expr)) + (cons first cont)) +;; #; ;; support for calls with any expression in operator position +;; (let ((args (cdr expr))) +;; (if (symbol? first) +;; (comp-call cte +;; args +;; (cons first cont)) +;; (comp-bind cte +;; '_ +;; first +;; ;#; ;; support for single expression in body +;; (cons '_ args) +;; #; ;; support for multiple expressions in body +;; (cons (cons '_ args) '()) +;; cont))) +)))) + + (else + ;; self-evaluating + (rib const-op expr cont)))) + +;#; ;; support for and, or, cond special forms +;;(define (build-if a b c) (cons 'if (list3 a b c))) +;;(define (list3 a b c) (cons a (list2 b c))) +;;(define (list2 a b) (cons a (list1 b))) +;;(define (list1 a) (cons a '())) + +(define (comp-bind cte var expr body cont) + (comp cte + expr + ;#; ;; support for single expression in body + (comp (cons var cte) + body + (if (eqv? cont tail) + cont + (rib jump/call-op ;; call + 'arg2 + cont))) +;; #; ;; support for multiple expressions in body +;; (comp-begin (cons var cte) +;; body +;; (if (eqv? cont tail) +;; cont +;; (rib jump/call-op ;; call +;; 'arg2 +;; cont))) +)) + +(define (comp-begin cte exprs cont) + (comp cte + (car exprs) + (if (pair? (cdr exprs)) + (rib jump/call-op ;; call + 'arg1 + (comp-begin cte (cdr exprs) cont)) + cont))) + +(define (gen-call v cont) + (if (eqv? cont tail) + (rib jump/call-op v 0) ;; jump + (rib jump/call-op v cont))) ;; call + +(define (gen-assign v cont) + (rib set-op v (gen-noop cont))) + +(define (gen-noop cont) + (if (and (rib? cont) ;; starts with pop? + (eqv? (field0 cont) jump/call-op) ;; call? + (eqv? (field1 cont) 'arg1) + (rib? (field2 cont))) + (field2 cont) ;; remove pop + (rib const-op 0 cont))) ;; add dummy value for set! + +(define (comp-call cte exprs nb-args var-cont) + (if (pair? exprs) + (comp cte + (car exprs) + (comp-call (cons #f cte) + (cdr exprs) + nb-args + var-cont)) + (let ((var (car var-cont))) + (let ((cont (cdr var-cont))) + (let ((v (lookup var cte 0))) + (add-nb-args + nb-args + (gen-call (if (and (not (rib? v)) ##feature-arity-check) (+ v 1) v) cont))))))) + +(define (lookup var cte i) + (if (pair? cte) + (if (eqv? (car cte) var) + i + (lookup var (cdr cte) (+ i 1))) + var)) + +(define (extend vars cte) + (if (pair? vars) + (cons (car vars) (extend (cdr vars) cte)) + cte)) + +(define tail (rib jump/call-op 'id 0)) ;; jump + +(define (compile expr) ;; converts an s-expression to a procedure + (make-procedure (rib 0 0 (comp '() expr tail)) '())) + +(define (eval expr) + ((compile expr))) + +(define (repl) + (putchar2 62 32) ;; #\> and space + (let ((expr (read))) + (if (eof-object? expr) + (newline) + (begin + (write (eval expr)) + (newline) + (repl))))) + +;;;---------------------------------------------------------------------------- From 60b27c4411dec517b6c6faa0f61ab5906612a841 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Thu, 29 Feb 2024 09:34:37 -0500 Subject: [PATCH 186/221] Count ribs interpreted --- alt/scheme/rvm.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 1956c17..109ac13 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -114,21 +114,23 @@ def run_compiled(encoded, interpreter, simulator, print_asm=DEFAULT_PRINT_ASM, t # FIXME: Ug instrs, symbols, stack_loc, pc_loc, next_rib_loc, interp_loop_addr, halt_loop_addr = assemble(encoded, interpreter, print_asm) - last_traced_exec = None symbols_by_addr = { addr: name for (name, addr) in symbols.items() } + last_traced_exec = None + ribs = 0 def trace(computer, cycles): - nonlocal last_traced_exec + nonlocal last_traced_exec, ribs inspector = Inspector(computer, symbols, stack_loc) if (trace_level >= TRACE_COARSE and (computer.pc == interp_loop_addr or computer.pc == halt_loop_addr)): if last_traced_exec is None: - print(f"{cycles:,d}:") + print(f"{ribs:,d}; cycle {cycles:,d}:") else: - print(f"{cycles:,d} (+{cycles - last_traced_exec:,d}):") + print(f"{ribs:,d}; cycle {cycles:,d} (+{cycles - last_traced_exec:,d}):") last_traced_exec = cycles + ribs += 1 print(f" stack ({inspector.show_addr(inspector.peek(stack_loc))}): {inspector.show_stack()}") From f80eeaf9d4e1d5cb3e99382176c6dc9869df8227 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Thu, 7 Mar 2024 11:58:14 -0500 Subject: [PATCH 187/221] =?UTF-8?q?Stash=20the=20head=20of=20the=20symbol?= =?UTF-8?q?=20table=20in=20the=20=E2=80=9Crib=E2=80=9D=20rib?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I missed this tricky detail until I actually watched it go wrong in the trace --- alt/scheme/rvm.py | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 109ac13..90074b1 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -187,24 +187,6 @@ def decode(input, asm): asm.comment("=== Data ===") - # Exactly one primitive proc rib is pre-defined: `rib` - asm.label("rib_rib") - asm.instr("#0") - asm.instr("#0") - asm.instr("#1") - - # Three constants the runtime can refer to by name: - def special(name): - asm.label(name) - asm.instr("#0") - asm.instr("#0") - asm.instr("#5") - special("rib_false") - special("rib_true") - special("rib_nil") - - asm.blank() - # for comprehension: pos = -1 def get_byte(): @@ -276,6 +258,29 @@ def emit_string(lbl, chars, count: int, comment): emit_rib(lbl, chars, f"#{coun asm.blank() + # Exactly one primitive proc rib is pre-defined: `rib` + # As a completely over-the-top hack, the location of the symbol table in RAM is stashed in + # the otherwise-unused second field. + # Note: can't emit this rib until the size of the symbol_table is known, hence the odd sequence. + asm.label("rib_rib") + asm.instr("#0") + asm.comment("Location of symtbl:") + # asm.instr(symbol_ref(0)[0]) + asm.instr(f"#{big.HEAP_BASE + 6*(len(sym_names)) - 3}") + asm.instr("#1") + asm.blank() + + # Three constants the runtime can refer to by name: + def special(name): + asm.label(name) + asm.instr("#0") + asm.instr("#0") + asm.instr("#5") + special("rib_false") + special("rib_true") + special("rib_nil") + asm.blank() + # TODO: move this table elsewhere, so the ribs for strings and instructions form a monolithic # block of address space? asm.comment("Table of pointers to symbol name and initial value ribs in ROM:") From 75468ca108b3d7ef387c28078b384336f46562a3 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 10 Mar 2024 15:56:05 -0400 Subject: [PATCH 188/221] Limit depth of lists; put top of stack on the left --- alt/scheme/inspector.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/alt/scheme/inspector.py b/alt/scheme/inspector.py index b8588d3..f8b62ca 100644 --- a/alt/scheme/inspector.py +++ b/alt/scheme/inspector.py @@ -65,14 +65,14 @@ def show_symbol(self, addr, with_value=False): val, name, z = self.peek(addr), self.peek(addr+1), self.peek(addr+2) assert z == 2 rib_num = (addr - big.HEAP_BASE)//3 - name_and_addr = f'"{self._obj(name)}"{self.show_addr(addr)}(_{rib_num})' + name_and_addr = f"{self._obj(name)}{self.show_addr(addr)}(_{rib_num})" if with_value: return f"{name_and_addr} = {self._obj(val)}" else: return name_and_addr - def _obj(self, val): + def _obj(self, val, max_depth=10): """Python representation of an object, which may be an integer, special value, or rib.""" # FIXME: check tag @@ -88,11 +88,15 @@ def _obj(self, val): x, y, z = self.peek(val), self.peek(val+1), self.peek(val+2) if z == 0: # pair car = self._obj(x) - cdr = self._obj(y) + if max_depth > 0: + cdr = self._obj(y, max_depth=max_depth-1) + else: + cdr = ["..."] if isinstance(cdr, list): return [car] + cdr else: - return (car, cdr) + # In at least one case, this is a "jump" instruction during compilation + return f"({car}, {cdr}, {x})" elif z == 1: # proc if 0 <= x < len(PRIMITIVES): return f"proc({PRIMITIVES[x]})" @@ -102,11 +106,10 @@ def _obj(self, val): elif z == 2: # symbol return f"symbol({self._obj(y)} = {self._obj(x)})" elif z == 3: # string - chars = self._obj(x) + chars = self._obj(x, max_depth=y) if len(chars) != y: - raise Exception("bad string") - else: - return "".join(chr(c) for c in chars) + print(f"bad string: {chars, y, z}") + return repr("".join(chr(c) for c in chars)) elif z == 4: # vector elems = self._obj(x) if not isinstance(elems, list) or len(elems) != y: @@ -166,7 +169,7 @@ def show_stack(self): """Show the contents of the stack, which is a list composed of ordinary pairs and continuation ribs.""" - return ", ".join(str(o) for o in self.stack()) + return ", ".join(str(o) for o in reversed(self.stack())) def list_str(lst): From c3ed61e2d59c1b4668bbf66ac1bd35d961de0271 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 10 Mar 2024 15:56:29 -0400 Subject: [PATCH 189/221] Catch exceptions from trace function --- alt/big.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/alt/big.py b/alt/big.py index 6019c98..e68fddb 100755 --- a/alt/big.py +++ b/alt/big.py @@ -310,6 +310,7 @@ def assemble(f, min_static=16, max_static=1023, builtins=BUILTIN_SYMBOLS): import computer import pygame.image +import sys import time @@ -339,6 +340,13 @@ def run(program, chip=BigComputer, simulator="codegen", name="Flat!", font="mona computer.poke(0, ROM_BASE) computer.poke(1, parse_op("0;JMP")) + def run_trace(): + try: + trace(computer, cycles) + except: + print(f"exception in trace: {sys.exc_info()[1]}") + + kvm = TextKVM(name, 80, 25, 6, 10, "alt/big/Monaco9.png") # TODO: use computer.py's "run", for many more features @@ -352,7 +360,7 @@ def run(program, chip=BigComputer, simulator="codegen", name="Flat!", font="mona halted = True print(f"\nHalted after {cycles:,d} cycles\n") if trace is not None: - trace(computer, cycles) + run_trace() if halted: @@ -367,8 +375,7 @@ def run(program, chip=BigComputer, simulator="codegen", name="Flat!", font="mona cycles += 1 if computer.fetch and trace is not None: - trace(computer, cycles) - + run_trace() if cycles % CYCLES_PER_CALL == 0 or halted: now = time.monotonic() From 596c57c3386ae59010fc1ceaecbce5afbceab4c9 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 10 Mar 2024 15:57:41 -0400 Subject: [PATCH 190/221] Debounce in getchar --- alt/scheme/Interpreter.jack | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/alt/scheme/Interpreter.jack b/alt/scheme/Interpreter.jack index 7bb4534..2b58d86 100644 --- a/alt/scheme/Interpreter.jack +++ b/alt/scheme/Interpreter.jack @@ -11,6 +11,9 @@ class Interpreter { // Pointers to the functions that handle each primitive: static Array handlers; + // Code of the key that was returned by getchar on the most recent call. + static int lastKeyPressed; + static Array tty; // DEBUG /* @@ -38,6 +41,8 @@ class Interpreter { // Bottom of the "heap" area: let nextRib = 32767 + 1; // Note: will overflow to -32768 + let lastKeyPressed = 0; + do Interpreter.initSymbolTable(); do Interpreter.initPrimitiveVectors(); @@ -504,7 +509,7 @@ class Interpreter { Note: this will only catch keypresses that occur after the instruction is executed. For a responsive shell, the check will have to incorporated into the interpreter loop. - It might even need to be be checked more often than once per instruction, since instructions + It might even need to be be checked more often than once per rib, since ribs can take as long as hundreds of cycles. */ function void handleGetchar() { @@ -512,11 +517,20 @@ class Interpreter { var int c; let keyboard = Jack.symbol("KEYBOARD"); - let c = 0; + + // First wait for the key to be different than the last call: + let c = keyboard[0]; + while (c = lastKeyPressed) { + let c = keyboard[0]; + } + + // Now wait for any key to be pressed: while (c = 0) { let c = keyboard[0]; } + let lastKeyPressed = c; + do Interpreter.push(c); return; From b32e214bb5d8109a62a975876e1513eb6771267c Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 10 Mar 2024 17:30:03 -0400 Subject: [PATCH 191/221] Show a blinking cursor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also: move handling of cursor position into the interpreter, along with putchar. It’s faster, avoids allocation, and it’s easy enough now that the Jack interpreter is the only game in town. --- alt/scheme/Interpreter.jack | 90 +++++++++++++++++++++++++++++++++---- alt/scheme/io.scm | 42 ++++++++--------- 2 files changed, 103 insertions(+), 29 deletions(-) diff --git a/alt/scheme/Interpreter.jack b/alt/scheme/Interpreter.jack index 2b58d86..0aeb1a4 100644 --- a/alt/scheme/Interpreter.jack +++ b/alt/scheme/Interpreter.jack @@ -11,11 +11,19 @@ class Interpreter { // Pointers to the functions that handle each primitive: static Array handlers; + // + // IO + // + + static int cursorX; + static int cursorY; + // Code of the key that was returned by getchar on the most recent call. static int lastKeyPressed; static Array tty; // DEBUG + /* When we arrive here, the assembly prelude has already initialized many of the statics with the starting values. @@ -41,6 +49,9 @@ class Interpreter { // Bottom of the "heap" area: let nextRib = 32767 + 1; // Note: will overflow to -32768 + // IO: + let cursorX = 0; + let cursorY = 0; let lastKeyPressed = 0; do Interpreter.initSymbolTable(); @@ -226,7 +237,7 @@ class Interpreter { let handlers[16] = Jack.symbol("interpreter.handleTimes"); let handlers[17] = Jack.symbol("interpreter.handleUnimp"); // quotient let handlers[18] = Jack.symbol("interpreter.handleGetchar"); - let handlers[19] = Jack.symbol("interpreter.handleUnimp"); // putchar + let handlers[19] = Jack.symbol("interpreter.handlePutchar"); let handlers[20] = Jack.symbol("interpreter.handlePeek"); let handlers[21] = Jack.symbol("interpreter.handlePoke"); let handlers[22] = Jack.symbol("interpreter.handleHalt"); @@ -505,7 +516,7 @@ class Interpreter { return; } - /* getchar :: -- (blocks until a key is pressed) + /* getchar :: -- (blocks until a key is pressed, and echos the character) Note: this will only catch keypresses that occur after the instruction is executed. For a responsive shell, the check will have to incorporated into the interpreter loop. @@ -515,27 +526,69 @@ class Interpreter { function void handleGetchar() { var Array keyboard; var int c; + var Array cursorAddr; let keyboard = Jack.symbol("KEYBOARD"); + let cursorAddr = Interpreter.screenAddr(cursorX, cursorY); + + let cursorAddr[0] = 0; // First wait for the key to be different than the last call: let c = keyboard[0]; while (c = lastKeyPressed) { let c = keyboard[0]; + let cursorAddr[0] = 95 - cursorAddr[0]; } // Now wait for any key to be pressed: while (c = 0) { let c = keyboard[0]; + let cursorAddr[0] = 95 - cursorAddr[0]; } let lastKeyPressed = c; + let cursorAddr[0] = c; + + if (c = 128) { + // newline, in the weird HACK keymap + let cursorX = 0; + let cursorY = cursorY + 1; + let c = 10; + } + else { + let cursorX = cursorX + 1; + } + // TODO: backspace? + // TODO: if we're at the bottom of the screen, scroll everything up do Interpreter.push(c); return; } + /* putchar :: c -- c (and draw the character at the current position) */ + function void handlePutchar() { + var int c; + var Array cursorAddr; + + // let c = Interpreter.peek(); + let c = stack[0]; + + if (c = 10) { + let cursorX = 0; + let cursorY = cursorY + 1; + // TODO: if we're at the bottom of the screen, scroll everything up + } + else { + let cursorAddr = Interpreter.screenAddr(cursorX, cursorY); + let cursorAddr[0] = c; + + let cursorX = cursorX + 1; + } + + return; + } + /* peek :: x -- RAM[x] */ function void handlePeek() { var int x, y, z; @@ -581,7 +634,10 @@ class Interpreter { TODO: implement function inlining and just use pop/peek/replace */ function void handleScreenAddr() { - var int x, y, acc0, acc1, acc2, acc3, acc4, acc5, acc6, addr; + var int x, y, addr; + // var int x, y, acc0, acc1, acc2, acc3, acc4, acc5, acc6, addr; + + // let tty[0] = -42; // let y = Interpreter.pop(); let y = stack[0]; @@ -589,6 +645,28 @@ class Interpreter { // let x = Interpreter.pop(); let x = stack[0]; // i.e. peek() + // // 80x = 8*(4x + x) = 2(2(2(2(2(2x) + x)))) + // let acc0 = y + y; // 2y + // let acc1 = acc0 + acc0; // 4y + // let acc2 = acc1 + y; // 5y + // let acc3 = acc2 + acc2; // 10y + // let acc4 = acc3 + acc3; // 20y + // let acc5 = acc4 + acc4; // 40y + // let acc6 = acc5 + acc5; // 80y + + // let addr = 2048 + acc6 + x; + + let addr = Interpreter.screenAddr(x, y); + + // do Interpreter.push(addr); + let stack[0] = addr; // replace(); + + return; + } + + function Array screenAddr(int x, int y) { + var int acc0, acc1, acc2, acc3, acc4, acc5, acc6; + // 80x = 8*(4x + x) = 2(2(2(2(2(2x) + x)))) let acc0 = y + y; // 2y let acc1 = acc0 + acc0; // 4y @@ -598,11 +676,7 @@ class Interpreter { let acc5 = acc4 + acc4; // 40y let acc6 = acc5 + acc5; // 80y - let addr = 2048 + acc6 + x; - // do Interpreter.push(addr); - let stack[0] = addr; // replace(); - - return; + return 2048 + acc6 + x; } /* Handler for any unused primitive code. */ diff --git a/alt/scheme/io.scm b/alt/scheme/io.scm index 3b04792..3420f79 100644 --- a/alt/scheme/io.scm +++ b/alt/scheme/io.scm @@ -16,29 +16,29 @@ (define screenAddr (rib 23 0 1)) (define (drawchar x y c) (poke (screenAddr x y) c)) -(define cursorx 0) -(define cursory 0) -(define (putchar c) - (if (eqv? c 10) - (begin - (set! cursorx 0) - (set! cursory (+ 1 cursory))) - (begin - (drawchar cursorx cursory c) - (set! cursorx (+ 1 cursorx))))) +;; (define cursorx 0) +;; (define cursory 0) +;; (define (putchar c) +;; (if (eqv? c 10) +;; (begin +;; (set! cursorx 0) +;; (set! cursory (+ 1 cursory))) +;; (begin +;; (drawchar cursorx cursory c) +;; (set! cursorx (+ 1 cursorx))))) ;; The getchar primitive just blocks and then returns a non-zero char. The repl seems to ;; expect getchar to handle echo, etc. ;; TODO: use a (let ...) here to hide this definition -(define getchar-primitive (rib 18 0 1)) -(define (getchar) - (let ((c (getchar-primitive))) - (if (eqv? c 128) ;; newline, according to the strange key mapping - (begin - (set! cursorx 0) - (set! cursory (+ 1 cursory)) - 10) ;; regular ASCII newline - (begin - (putchar c) - c)))) +;; (define getchar-primitive (rib 18 0 1)) +;; (define (getchar) +;; (let ((c (getchar-primitive))) +;; (if (eqv? c 128) ;; newline, according to the strange key mapping +;; (begin +;; (set! cursorx 0) +;; (set! cursory (+ 1 cursory)) +;; 10) ;; regular ASCII newline +;; (begin +;; (putchar c) +;; c)))) From f4b1229a17504918f2f807677a7aa4da761f40ec Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 10 Mar 2024 17:31:35 -0400 Subject: [PATCH 192/221] Use Jack interpreter by default --- alt/scheme/rvm.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 90074b1..7b0afb6 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -1824,7 +1824,7 @@ def main(): parser.add_argument("--trace", action="store_true", help="Print each Ribbit instruction as it is interpreted. Note: runs almost 3x slower.") parser.add_argument("--print", action="store_true", help="Print interpreter assembly and compiled instructions.") # TEMP: experimental for now - parser.add_argument("--jack", action="store_true", help="Use the Jack interpreter.") + parser.add_argument("--asm", action="store_true", help="Use the (partially-implemented) assembly interpreter.") args = parser.parse_args() @@ -1834,10 +1834,11 @@ def main(): src_lines += [] + f.readlines() run("".join(src_lines), - interpreter="jack" if args.jack else "assembly", + interpreter="jack" if not args.asm else "assembly", simulator=args.simulator, print_asm=args.print, trace_level=TRACE_COARSE if args.trace else TRACE_NONE) + # trace_level=TRACE_FINE if args.trace else TRACE_NONE) if __name__ == "__main__": From cdc1cbf9cf16ce443776ad3accb2076194b260cc Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Mon, 11 Mar 2024 11:37:23 -0400 Subject: [PATCH 193/221] Update head comment --- alt/scheme/rvm.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 7b0afb6..9f2a55b 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -2,10 +2,12 @@ """Ribbit VM implementation. -The interpreter and primitives are all hand-rolled assembly. In hindsight, it would have been -quicker and easier to adapt the compiler in "register.py", with a minimal library, and implement the -interpreter in that. The size in ROM of the actual interpreter turns out not to be very critical -(~1K words), and we're not really going for maximum performance here. +Two interpreters are currently implemented: +- a faster, more compact, but incomplete interpreter, in hand-rolled assembly +- a slightly slower, larger, but almost completely functional version in Jack using the compiler + in reg.py, which has been significantly enhanced for this purpose. + +Neither interpreter provides garbage-collection yet. RSC's encoded instruction stream is decoded (in Python) into ribs directly in the ROM, which is both relatively efficient in time and space, and more realistic than loading a complex program From f131333a16087d8c9e13676233bab814d6f4334b Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Mon, 11 Mar 2024 12:09:19 -0400 Subject: [PATCH 194/221] Exports seem to work: remove an old hack --- alt/scheme/repl.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/alt/scheme/repl.py b/alt/scheme/repl.py index c47155f..084ec75 100755 --- a/alt/scheme/repl.py +++ b/alt/scheme/repl.py @@ -22,8 +22,6 @@ def load(path): ;; TEMP: this should be good enough to convert single-digit numbers to strings for display (define (quotient x y) 0) -;; TEMP: until I get exports to work -(define + (rib 14 0 1)) (repl) From b5158cee6a54678f1c02f61c3492798b62a2670c Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Mon, 11 Mar 2024 12:09:49 -0400 Subject: [PATCH 195/221] Update for many changes --- alt/scheme/README.md | 59 ++++++++++++++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 18 deletions(-) diff --git a/alt/scheme/README.md b/alt/scheme/README.md index d79f8f6..f1d191b 100644 --- a/alt/scheme/README.md +++ b/alt/scheme/README.md @@ -17,36 +17,39 @@ A source program is compiled by the Ribbit AOT compiler to an instruction graph [A Small Scheme VM, Compiler, and REPL in 4K](http://www.iro.umontreal.ca/~feeley/papers/YvonFeeleyVMIL21.pdf), with a few modifications. -Unimplemented primitives: -- `*`, `quotient`: the Hack CPU does not provide these; they can be implemented as ordinary - functions (*TBD* probably need at least `*` as a primitive for speed). -- `getchar` and `putchar`: there is no OS to provide these, they will be implemented as ordinary - functions when needed (i.e. for the REPL) - Additional primitives: - `peek`; code: `20`; `x ← pop(); r ← RAM[x]` - `poke`; code: `21`; `y ← pop(); x ← pop(); RAM[x] ← y; r <- y` -- `halt`; code: `22`; stop the machine, probably by going into a tight loop +- `halt`; code: `22`; stop the machine, by going into a tight loop recognized by the simulator +- `screenAddr`; code: `23`; `y <- pop(); x <- pop(); r <- address of character at (x, y)` `peek` and `poke` can be used to read and write to any address, but most usefully the screen buffer (which is at 0x0400–0x0x07E7) and the location mapped to the keyboard (0x07FF). `halt` can be used to signal normal completion of a program. +`screenAddr` provides a fast implementation of the very common calculation mapping coordinates +to the screen buffer in memory. + + ## Memory Layout -| Address | Contents | Description | -| 0 | SP | Address of the cons list that represents the stack: i.e. a "pair" rib which contains the value at the top of the stack and points to the next entry. | -| 1 | PC | Address of the rib currently being interpreted. | -| 2 | NEXT_RIB | Address where the next allocation will occur. | +| Address | Label | Contents | Description | +| 0 | `interpreter.stack` | SP | Address of the cons list that represents the stack: i.e. a "pair" rib which contains the value at the top of the stack and points to the next entry. | +| 1 | `interpreter.pc` | PC | Address of the rib currently being interpreted. | +| 2 | `interpreter.nextRib` | NEXT_RIB | Address where the next allocation will occur. | | ... | | TBD: values used by the garbage collector to keep track of free space. | +*Note: when the Jack interpreter is used, these locations are determined by the assembler, +addresses 0-15 and are used by the interpreter's own stack pointers and temporary storage, and the +interpreter's own stack grows up from address 256.* + ## Rib Representation For simplicity, ribs are stored as described in the paper, three words each. -The high bit of each word is used as a tag to identify whether the word points to a rib. +**TODO**: The high bit of each word is used as a tag to identify whether the word points to a rib. - A word with the high bit set is a 15-bit signed integer value. To recover an ordinary 16-bit value when needed, bit 14 is copied to bit 15. For many operations, it's sufficient to treat values as unsigned and just make sure to set the high bit if it might have been cleared (e.g. due to overflow.) @@ -79,17 +82,37 @@ implementations. That means to initialize we need something like 6K of heap plus some for stack. Actual size in the implementation: -- Instruction ribs in the ROM for the REPL: 1643 (4.8K words) -- Initial ribs in RAM for the symbol table: 212 (0.6K words) -- Note: this does not account for additional Scheme code that will eventually be needed to make it - work (e.g. getchar/putchar). +- Instruction ribs in the ROM for the REPL: 1,340 (4.0K words) +- String/char ribs in R0M for the symbol table: 1,170 (3.5K words) +- Initial ribs in RAM for the symbol table: 89 (267 words) +- Actual total ROM, including interpreter and tables: at least 11K. + -[A comment in the Ribbit source]((https://github.com/udem-dlteam/ribbit/blob/dev/src/host/c/rvm.c#L207)) +[A comment in the Ribbit source](https://github.com/udem-dlteam/ribbit/blob/dev/src/host/c/rvm.c#L207) suggests that space for 48,000 ribs is needed to bootstrap. Presumably that refers to running the *compiler*, which is out of scope. + ## GC The program runs until memory is exhausted. -TODO: collect garbage. +**TODO**: collect garbage. + +See https://github.com/udem-dlteam/ribbit/issues/26. + + +## Performance + +With the current Jack compiler and the `compiled` simulator, performance of the interpreter is good +enough to call interactive. However, if and when we decide to improve it, there are a couple of +obvious bottlenecks: + +- Symbol table lookup: the REPL's symbol table is at least 89 deep, and linear search in interpreted + Scheme is currently consuming a large portion of the total time. Could experiment with a simple tree + structure, which would be roughly log(90) = 6 levels deep. + +- Dispatching on instruction and primitive codes is relatively expensive. Could embed handler pointers + directly in the ribs? Or just use more codes to flatten the amount of dispatching that's currently + needed (e.g. separate codes for call slot, call symbol, jump to slot, jump to symbol). Either would + require modifying the Scheme compiler to account for the new representation. From d1bae3721db75a7a560a2956aa9d0766823e9676 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Tue, 12 Mar 2024 23:53:17 -0400 Subject: [PATCH 196/221] Manually disable the ##feature-arity-check flag --- alt/scheme/ribbit/min.scm | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/alt/scheme/ribbit/min.scm b/alt/scheme/ribbit/min.scm index 9b53bf5..d676262 100644 --- a/alt/scheme/ribbit/min.scm +++ b/alt/scheme/ribbit/min.scm @@ -722,8 +722,10 @@ (define const-op 3) (define if-op 4) +(define feature-arity-check #f) ;; HACK + (define (add-nb-args nb tail) - (if ##feature-arity-check + (if feature-arity-check ;; HACK (rib const-op nb tail) @@ -927,7 +929,7 @@ (let ((v (lookup var cte 0))) (add-nb-args nb-args - (gen-call (if (and (not (rib? v)) ##feature-arity-check) (+ v 1) v) cont))))))) + (gen-call (if (and (not (rib? v)) feature-arity-check) (+ v 1) v) cont))))))) ;; HACK (define (lookup var cte i) (if (pair? cte) From 51f012db00694a9be635c703d9acbd859448ae88 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Tue, 12 Mar 2024 23:53:57 -0400 Subject: [PATCH 197/221] =?UTF-8?q?Fix=20typo:=20was=20logging=20wrong=20f?= =?UTF-8?q?ield=20of=20unrecognized=20=E2=80=9Cpair=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- alt/scheme/inspector.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alt/scheme/inspector.py b/alt/scheme/inspector.py index f8b62ca..8bbf827 100644 --- a/alt/scheme/inspector.py +++ b/alt/scheme/inspector.py @@ -96,7 +96,7 @@ def _obj(self, val, max_depth=10): return [car] + cdr else: # In at least one case, this is a "jump" instruction during compilation - return f"({car}, {cdr}, {x})" + return f"({car}, {cdr}, {z})" elif z == 1: # proc if 0 <= x < len(PRIMITIVES): return f"proc({PRIMITIVES[x]})" From befeb970cf55685720ee683736a6a087edacc412 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 17 Mar 2024 13:18:27 -0400 Subject: [PATCH 198/221] Line editing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The first call to getchar captures an entire line of input, then pushes the characters one at a time. This makes the REPL behave in a sensible way, and can handle backspace. It also means that most text entry is always happening when the interpreter is in a type loop handling getchar, so it’s much more responsive. --- alt/scheme/Interpreter.jack | 133 +++++++++++++++++++++++++----------- 1 file changed, 94 insertions(+), 39 deletions(-) diff --git a/alt/scheme/Interpreter.jack b/alt/scheme/Interpreter.jack index 0aeb1a4..605116f 100644 --- a/alt/scheme/Interpreter.jack +++ b/alt/scheme/Interpreter.jack @@ -18,6 +18,9 @@ class Interpreter { static int cursorX; static int cursorY; + static Array bufferStart; + static Array bufferEnd; + // Code of the key that was returned by getchar on the most recent call. static int lastKeyPressed; @@ -49,15 +52,12 @@ class Interpreter { // Bottom of the "heap" area: let nextRib = 32767 + 1; // Note: will overflow to -32768 - // IO: - let cursorX = 0; - let cursorY = 0; - let lastKeyPressed = 0; - do Interpreter.initSymbolTable(); do Interpreter.initPrimitiveVectors(); + do Interpreter.initIO(); + // Bottom of the stack: "primordial continuation" in ROM let stack = Jack.symbol("rib_outer_cont"); @@ -256,6 +256,18 @@ class Interpreter { return; } + function void initIO() { + // Allocate space for one full line of characters, just below where the primitive vectors are stored + let bufferStart = handlers - 80; // a constant + let bufferEnd = bufferStart; // ptr to the word *after* the last word containing a character + + let cursorX = 0; + let cursorY = 0; + let lastKeyPressed = 0; + + return; + } + /* rib :: x y z -- rib(x, y, z) */ function void handleRib() { var int x, y, z; @@ -516,54 +528,97 @@ class Interpreter { return; } - /* getchar :: -- (blocks until a key is pressed, and echos the character) + /* getchar :: -- (blocks an entire line is entered, while echoing characters) + + Once a line is captured, multiple calls will return successive characters until all are + consumed. Then the next call will block again. - Note: this will only catch keypresses that occur after the instruction is executed. For - a responsive shell, the check will have to incorporated into the interpreter loop. - It might even need to be be checked more often than once per rib, since ribs - can take as long as hundreds of cycles. + Note: this will only catch keypresses that occur after the instruction is executed. */ function void handleGetchar() { var Array keyboard; - var int c; + var int c, underscore; var Array cursorAddr; - let keyboard = Jack.symbol("KEYBOARD"); - let cursorAddr = Interpreter.screenAddr(cursorX, cursorY); + if (~(bufferEnd > bufferStart)) { + let keyboard = Jack.symbol("KEYBOARD"); + let underscore = 95; - let cursorAddr[0] = 0; + while ((bufferEnd = bufferStart) | ~(c = 10)) { - // First wait for the key to be different than the last call: - let c = keyboard[0]; - while (c = lastKeyPressed) { - let c = keyboard[0]; - let cursorAddr[0] = 95 - cursorAddr[0]; - } + let cursorAddr = Interpreter.screenAddr(cursorX, cursorY); + let cursorAddr[0] = 0; - // Now wait for any key to be pressed: - while (c = 0) { - let c = keyboard[0]; - let cursorAddr[0] = 95 - cursorAddr[0]; - } + // First wait for the key to be different than the last call/iteration: + let c = keyboard[0]; + while (c = lastKeyPressed) { + let cursorAddr[0] = underscore - cursorAddr[0]; // blink + let c = keyboard[0]; + } - let lastKeyPressed = c; - let cursorAddr[0] = c; + // Now wait for any key to be pressed: + while (c = 0) { + let cursorAddr[0] = underscore - cursorAddr[0]; // blink + let c = keyboard[0]; + } - if (c = 128) { - // newline, in the weird HACK keymap - let cursorX = 0; - let cursorY = cursorY + 1; - let c = 10; + let lastKeyPressed = c; + + if (c = 128) { + // newline, in the weird HACK keymap + + // Clear the blicking cursor: + let cursorAddr[0] = 0; + + //move the cursor to the next line: + let cursorX = 0; + let cursorY = cursorY + 1; + + // Map to regular newline + let c = 10; + + // Add the + let bufferEnd[0] = c; + let bufferEnd = bufferEnd + 1; + } + else { if (c = 129) { + // backspace + if (bufferEnd > bufferStart) { + // Clear the blinking cursor: + let cursorAddr[0] = 0; + + // Move left and delete the last character from the buffer: + let cursorX = cursorX - 1; + let bufferEnd = bufferEnd - 1; + } + } + else { if (cursorX < 80) { + let cursorAddr[0] = c; + let cursorX = cursorX + 1; + let bufferEnd[0] = c; + let bufferEnd = bufferEnd + 1; + }}} + // TODO: if we're at the bottom of the screen, scroll everything up + } + + // An entire line has been captured; yield the first character and return: + do Interpreter.push(bufferStart[0]); + let bufferStart = bufferStart + 1; + + return; } else { - let cursorX = cursorX + 1; + // Some characters were captured previously, so return the next in the sequence: + let c = bufferStart[0]; + let bufferStart = bufferStart+1; + if (~(bufferStart < bufferEnd)) { + // No more characters after this; reset the buffer + let bufferStart = handlers - 80; + let bufferEnd = bufferStart; + } + do Interpreter.push(c); + return; } - // TODO: backspace? - // TODO: if we're at the bottom of the screen, scroll everything up - - do Interpreter.push(c); - - return; } /* putchar :: c -- c (and draw the character at the current position) */ From ef47d3681484c6d134f4e09d416611624562a610 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 17 Mar 2024 13:23:25 -0400 Subject: [PATCH 199/221] Update decsription of getchar and memory layout --- alt/scheme/README.md | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/alt/scheme/README.md b/alt/scheme/README.md index f1d191b..419425e 100644 --- a/alt/scheme/README.md +++ b/alt/scheme/README.md @@ -31,14 +31,31 @@ Additional primitives: `screenAddr` provides a fast implementation of the very common calculation mapping coordinates to the screen buffer in memory. +Note: `getchar` reads characters one at a time from a buffer which contains up to an entire line. +No characters are returned until `newline` (HACK: 128; Scheme: 10) is entered. As long as a line +hasn't been completed, the backspace key can be used to edit the line by removing the last-entered +character (if any). +However, at present, characters can only be entered after `getchar` has been invoked. While in the +loop receiving keypresses, a "blinking" underscore indicates the cursor position. +This makes `getchar`/`putchar` unsuitable for non-terminal-oriented uses (i.e. games.) Instead, you +can implement your own "keyDown" and "drawChar" operations (see [io.scm](io.scm)) using +`(peek 4095)` and `(poke (screenAddr x y) c)`. + ## Memory Layout -| Address | Label | Contents | Description | -| 0 | `interpreter.stack` | SP | Address of the cons list that represents the stack: i.e. a "pair" rib which contains the value at the top of the stack and points to the next entry. | -| 1 | `interpreter.pc` | PC | Address of the rib currently being interpreted. | -| 2 | `interpreter.nextRib` | NEXT_RIB | Address where the next allocation will occur. | -| ... | | TBD: values used by the garbage collector to keep track of free space. | +| Address | Label | Contents | Description | +| 0 | `interpreter.static_stack` | SP | Address of the cons list that represents the stack: i.e. a "pair" rib which contains the value at the top of the stack and points to the next entry. | +| 1 | `interpreter.static_pc` | PC | Address of the rib currently being interpreted. | +| 2 | `interpreter.static_nextRib` | NEXT_RIB | Address where the next allocation will occur. | +| ... | | | TBD: values used by the garbage collector to keep track of free space. | +| 256– | | | Jack stack. | +| 1936–2015 | `interpreter.static_bufferStart`/`End` | | Space to store a line of input from the keyboard. | +| 2016–2047 | `interpreter.static_handlers` | | Function pointer for each primitive handler. | +| 2048-4048 | | | Screen buffer: 80x25 characters. | +| 4095 | | KEYBOARD | Mapped to the keyboard for input and "tty" for output. | +| 4096–32767 | | | ROM: interpreter, symbol names, instructions. | +| 32768–65536 | | | Heap. | *Note: when the Jack interpreter is used, these locations are determined by the assembler, addresses 0-15 and are used by the interpreter's own stack pointers and temporary storage, and the From dc19911711c213aead29879a93b514a1a1cf3ff2 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 17 Mar 2024 13:27:17 -0400 Subject: [PATCH 200/221] Echo a line at a time, to show the new behavior --- alt/scheme/example/echo.scm | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/alt/scheme/example/echo.scm b/alt/scheme/example/echo.scm index c71b42b..cb7ca89 100644 --- a/alt/scheme/example/echo.scm +++ b/alt/scheme/example/echo.scm @@ -1,8 +1,10 @@ -;; Note: io.scm should be loaded first +;; Read and echo input line by line. +;; Note: getchar buffers the input until a whole line is entered; this script just has to read +;; and write the characters one at a time. (define (echo) (let ((c (getchar))) - ;;(putchar c) + (putchar c) (echo))) -(echo) \ No newline at end of file +(echo) From c466b59084b16e4744d9e60e997a9bfa98cfb42d Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 17 Mar 2024 13:35:22 -0400 Subject: [PATCH 201/221] Implement quotient; fixing int->string --- alt/scheme/Interpreter.jack | 74 ++++++++++++++++++++++++++++++++++++- alt/scheme/inspector.py | 4 +- alt/scheme/repl.py | 20 ++++------ 3 files changed, 83 insertions(+), 15 deletions(-) diff --git a/alt/scheme/Interpreter.jack b/alt/scheme/Interpreter.jack index 605116f..7e2c78c 100644 --- a/alt/scheme/Interpreter.jack +++ b/alt/scheme/Interpreter.jack @@ -235,7 +235,7 @@ class Interpreter { let handlers[14] = Jack.symbol("interpreter.handlePlus"); let handlers[15] = Jack.symbol("interpreter.handleMinus"); let handlers[16] = Jack.symbol("interpreter.handleTimes"); - let handlers[17] = Jack.symbol("interpreter.handleUnimp"); // quotient + let handlers[17] = Jack.symbol("interpreter.handleQuotient"); let handlers[18] = Jack.symbol("interpreter.handleGetchar"); let handlers[19] = Jack.symbol("interpreter.handlePutchar"); let handlers[20] = Jack.symbol("interpreter.handlePeek"); @@ -528,6 +528,26 @@ class Interpreter { return; } + + /* quotient :: x y -- x / y */ + function void handleQuotient() { + var int x, y, z; + var Rib tmp; + + // Note: one rib becomes garbage: + // let y = Interpreter.pop(); + let y = stack[0]; + let stack = stack[1]; + + let x = stack[0]; + + // Update second stack entry in place: + let stack[0] = Interpreter.divide(x, y); + + return; + } + + /* getchar :: -- (blocks an entire line is entered, while echoing characters) Once a line is captured, multiple calls will return successive characters until all are @@ -857,6 +877,58 @@ class Interpreter { } } + /** + This was lifted verbatim from Math.jack in the solutions for project 12 from the book, except + for inlining abs(). + Does that mean it's good? Maybe, but it probably works. + */ + function int divide(int x, int y) { + var int localX, localY; + var boolean neg; + var int q, r; + + if (y = 0) { + do Interpreter.halt(); + } + + // Get sign, then take absolute values (inline to save fn calls): + // Note: testing each operand only once to minimize branching. + let neg = false; + if (localY < 0) { + let localY = -localY; + let neg = ~neg; + } + if (localX < 0) { + let localX = -localX; + let neg = ~neg; + } + + // No more bits to look at: + if (y > x) { + return 0; + } + + // Try dividing by 2y: + if ((y+y) < 0) { + // If 2*y overflows, it's definitely > x. + let q = 0; + } + else { + let q = Interpreter.divide(x, y+y); + } + let r = q+q; + if (~((x - Interpreter.multiply(r, y)) < y)) { + let r = r + 1; + } + + if (neg) { + return -r; + } + else { + return r; + } + } + /** Allocate a rib on the heap, filling in the three fields. */ function Rib alloc(int x, int y, int z) { var Rib r; diff --git a/alt/scheme/inspector.py b/alt/scheme/inspector.py index 8bbf827..ac61227 100644 --- a/alt/scheme/inspector.py +++ b/alt/scheme/inspector.py @@ -198,9 +198,9 @@ def list_str(lst): 14: "+", 15: "-", 16: "*", - 17: "quotient (unimp.)", + 17: "quotient", 18: "getchar", - 19: "putchar (unimp.)", + 19: "putchar", # Extra: 20: "peek", 21: "poke", diff --git a/alt/scheme/repl.py b/alt/scheme/repl.py index 084ec75..c714742 100755 --- a/alt/scheme/repl.py +++ b/alt/scheme/repl.py @@ -19,23 +19,19 @@ def load(path): program = "".join(min_library_src_lines + io_src_lines) + """ -;; TEMP: this should be good enough to convert single-digit numbers to strings for display -(define (quotient x y) 0) - - (repl) ;; Exported symbols. (export - -* -+ -- -< -= -cons -) + + + - + * + quotient + < + = + cons + ) """ # Note: actually running the compiler in the Ribbit Python interpreter is pretty slow. From 67208c2647ce8959a7d86e9e672e0f24571a6b5b Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 17 Mar 2024 14:00:31 -0400 Subject: [PATCH 202/221] Elide unreachable jump after return --- alt/reg.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/alt/reg.py b/alt/reg.py index 49153a5..86f810c 100755 --- a/alt/reg.py +++ b/alt/reg.py @@ -1665,8 +1665,9 @@ def handle_If(self, ast: If): for s in ast.when_true: self._handle(s) - self.asm.instr(f"@{end_label}") - self.asm.instr(f"0;JMP") + if not isinstance(ast.when_true[-1], Return): + self.asm.instr(f"@{end_label}") + self.asm.instr(f"0;JMP") self.asm.label(false_label) for s in ast.when_false: From 8489219addb5accdfe0b22fb13ec1edc896239ef Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Sun, 17 Mar 2024 14:00:44 -0400 Subject: [PATCH 203/221] Remove some spurious comments --- alt/reg.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/alt/reg.py b/alt/reg.py index 86f810c..2a8b274 100755 --- a/alt/reg.py +++ b/alt/reg.py @@ -1632,8 +1632,6 @@ def handle_Store(self, ast: Store): self.asm.instr("M=D") def handle_If(self, ast: If): - self.asm.comment("if...") - if ast.when_false is None: # Awesome: when there's no else, and the condition is simple, it turns into a single branch. # TODO: to avoid constructing boolean values, probably want to put left _and_ right values @@ -1719,8 +1717,8 @@ def handle_Return(self, ast: Return): self.asm.instr(f"@{RESULT}") self.asm.instr("M=D") - self.asm.start("return (from leaf)") if self.leaf_sub_args is not None: + self.asm.start("return (from leaf)") if self.leaf_sub_args > 0: if self.leaf_sub_args == 1: self.asm.comment("pop 1 argument") From af45ee290fc0d315114e7560e3699caa69360439 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Thu, 21 Mar 2024 12:04:25 -0400 Subject: [PATCH 204/221] Neevermind with locals; factor out recursive divide with fewer redundant checks --- alt/scheme/Interpreter.jack | 36 ++++++++++++++++++++++++++++++------ alt/scheme/test_rvm.py | 24 +++++++++++++++++++++++- 2 files changed, 53 insertions(+), 7 deletions(-) diff --git a/alt/scheme/Interpreter.jack b/alt/scheme/Interpreter.jack index 7e2c78c..91f3e5c 100644 --- a/alt/scheme/Interpreter.jack +++ b/alt/scheme/Interpreter.jack @@ -883,7 +883,6 @@ class Interpreter { Does that mean it's good? Maybe, but it probably works. */ function int divide(int x, int y) { - var int localX, localY; var boolean neg; var int q, r; @@ -894,12 +893,12 @@ class Interpreter { // Get sign, then take absolute values (inline to save fn calls): // Note: testing each operand only once to minimize branching. let neg = false; - if (localY < 0) { - let localY = -localY; + if (y < 0) { + let y = -y; let neg = ~neg; } - if (localX < 0) { - let localX = -localX; + if (x < 0) { + let x = -x; let neg = ~neg; } @@ -914,7 +913,7 @@ class Interpreter { let q = 0; } else { - let q = Interpreter.divide(x, y+y); + let q = Interpreter.dividePos(x, y+y); } let r = q+q; if (~((x - Interpreter.multiply(r, y)) < y)) { @@ -929,6 +928,31 @@ class Interpreter { } } + /** Divide x by y, assuming both a positive. */ + function int dividePos(int x, int y) { + var int q, r; + + // No more bits to look at: + if (y > x) { + return 0; + } + + // Try dividing by 2y: + if ((y+y) < 0) { + // If 2*y overflows, it's definitely > x. + let q = 0; + } + else { + let q = Interpreter.dividePos(x, y+y); + } + let r = q+q; + if (~((x - Interpreter.multiply(r, y)) < y)) { + let r = r + 1; + } + + return r; + } + /** Allocate a rib on the heap, filling in the three fields. */ function Rib alloc(int x, int y, int z) { var Rib r; diff --git a/alt/scheme/test_rvm.py b/alt/scheme/test_rvm.py index dcfeccd..e539894 100755 --- a/alt/scheme/test_rvm.py +++ b/alt/scheme/test_rvm.py @@ -93,6 +93,26 @@ def test_mul_mixed_signs(run): assert output == [] +@parameterize +def test_quotient(run): + program = "(quotient 123 10)" + + inspect, output = run(program) + + assert inspect.stack() == [12] + assert output == [] + + +@parameterize +def test_quotient_mixed_signs(run): + program = several("(quotient 6 -3)", "(quotient -14 -3)", "(* 21 -2)") + + inspect, output = run(program) + + assert inspect.stack() == [[-2, 4, -10]] + assert output == [] + + @parameterize def test_if(run): program = "(if #t 42 '())" @@ -368,7 +388,7 @@ def run_to_halt(program, interpreter, max_cycles=20000, simulator="codegen"): encoded = rvm.compile(program) # print(f"encoded program: {repr(encoded)}") - instrs, symbols, stack_loc, pc_loc, next_rib_loc, interp_loop_addr, halt_loop_addr = rvm.assemble(encoded, interpreter, False) + instrs, symbols, stack_loc, pc_loc, next_rib_loc, interp_loop_addr, halt_loop_addr = rvm.assemble(encoded, interpreter, True) # asm = AssemblySource() @@ -420,4 +440,6 @@ def run_to_halt(program, interpreter, max_cycles=20000, simulator="codegen"): print(f" pc: {inspect.show_addr(pc)}") print(f" {inspect.show_instr(pc)}") + print(f"cycles: {cycles}") + return (inspect, output) From c61583ba68b577bd5e1a2b7e62b24d65fdafec08 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Thu, 21 Mar 2024 12:15:17 -0400 Subject: [PATCH 205/221] Show mem consumption when not tracing --- alt/big.py | 5 +++-- alt/scheme/rvm.py | 13 ++++++++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/alt/big.py b/alt/big.py index e68fddb..d27a455 100755 --- a/alt/big.py +++ b/alt/big.py @@ -321,7 +321,7 @@ def assemble(f, min_static=16, max_static=1023, builtins=BUILTIN_SYMBOLS): CYCLES_PER_CALL = 100 # Number of cycles to run in the tight loop (when not tracing) -def run(program, chip=BigComputer, simulator="codegen", name="Flat!", font="monaco-9", halt_addr=None, trace=None, verbose_tty=True): +def run(program, chip=BigComputer, simulator="codegen", name="Flat!", font="monaco-9", halt_addr=None, trace=None, verbose_tty=True, meters=None): """Run with keyboard and text-mode graphics.""" # TODO: font @@ -407,7 +407,8 @@ def run_trace(): cps = (cycles - last_cycle_count)/(now - last_cycle_time) msgs.append(f"{cps/1000:0,.1f}k/s") msgs.append(f"@{computer.pc}") - # TODO: let the caller add meters (e.g. Ribbit heap consumption) + if meters: + msgs.extend(meters(computer, cycles)) pygame.display.set_caption(f"{name}: {'; '.join(msgs)}") last_cycle_time = now diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 9f2a55b..16ea55a 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -154,12 +154,23 @@ def trace(computer, cycles): elif trace_level >= TRACE_ALL: print(f"{cycles:3,d}: {computer.pc}") + def meters(computer, cycles): + inspector = Inspector(computer, symbols, stack_loc) + next_rib = unsigned(inspector.peek(next_rib_loc)) + current_ribs = (next_rib - big.HEAP_BASE)//3 + max_ribs = (big.HEAP_TOP - big.HEAP_BASE)//3 + return { + f"mem: {100*current_ribs/max_ribs:0.1f}%" + } + + big.run(program=instrs, simulator=simulator, name="Scheme", halt_addr=halt_loop_addr, trace=trace if trace_level > TRACE_NONE else None, - verbose_tty=verbose_tty) + verbose_tty=verbose_tty, + meters=meters) def compile(src): From 29d7bbc7fbac4962175b7a216d507a46ef7bbea3 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Mon, 25 Mar 2024 10:52:30 -0400 Subject: [PATCH 206/221] Add screen capture of the REPL --- alt/scheme/README.md | 8 +++++--- alt/scheme/capture.mov | Bin 0 -> 1210408 bytes 2 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 alt/scheme/capture.mov diff --git a/alt/scheme/README.md b/alt/scheme/README.md index 419425e..63e78f6 100644 --- a/alt/scheme/README.md +++ b/alt/scheme/README.md @@ -7,9 +7,11 @@ Goals: - provide a REPL that can compile and run simple functions like `fib` or `fact` written at the keyboard - run something graphical (even if in character-mode) -- implement as little as possible in assembly; just the bare interpreter and Ribbit primitives, +- implement as little as possible in assembly/Jack; just the bare interpreter and Ribbit primitives, plus a few additions for accessing the hardware. +![evaluating (+ 1 2)](capture.mov) + ## Virtual Machine @@ -38,8 +40,8 @@ character (if any). However, at present, characters can only be entered after `getchar` has been invoked. While in the loop receiving keypresses, a "blinking" underscore indicates the cursor position. This makes `getchar`/`putchar` unsuitable for non-terminal-oriented uses (i.e. games.) Instead, you -can implement your own "keyDown" and "drawChar" operations (see [io.scm](io.scm)) using -`(peek 4095)` and `(poke (screenAddr x y) c)`. +can implement your own "keyDown" and "drawChar" operations using `(peek 4095)` and +`(poke (screenAddr x y) c)` (see [io.scm](io.scm)). ## Memory Layout diff --git a/alt/scheme/capture.mov b/alt/scheme/capture.mov new file mode 100644 index 0000000000000000000000000000000000000000..885b9c2d5e6fea7405a0206efc22bc59fc0a0cf1 GIT binary patch literal 1210408 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
    1y~A0*YX>{TCNCxILF&R>uK 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%0D;CU?a&b7 zdr$*{*&h%z=iX0PSd|jVG^pO%crsO;DqN`U4o*`r&#mnw{tE5x!xksmuA!3Mr4TinKUODo3Kpf zY5dV_%ou!sB4#NYAg{K~+#pt)4E^nz1@iC(x5M!&mo)}Oky$=-Wx2+h+_XTcp6P0h ze7@gys+TW+so-r7cJCPm@BEnRq_W98cK2b-xNNh565!Li-AvG)(8n659u*?d=ntG{ zVETB4`+n>{g2rNTa7zƟi!y2XJsh#Bxn+!qtpza<+Ev`}Hryx-AfW0r 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�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 z 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

    u^xM72I0- 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 z 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

    Y(!)jZVf(rCg!QBw*7%z^wt2(2 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

    +Cg9j~ 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

    nIeifc)wU0yadEUQ++7o*Uw4B9v`1suX) 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

    #gVnq#l+C}E!gVUckPhGG^M@AoBf_cxu0L0 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�vl#xxl_a(UtSxg9XFqT3iPibcRw9qn9j^%Je=631TXc2 zGkq&}E_eVw&;Mn6HQ&CQ<|U_ZadMQGjjTnAh+zxnd4T42ts>q!9{JG|ZQw)?- zhXA+v`d554D~a%yD9H${l#!{^=;C?wbl44+K@Nj>4_$N4H{V?NK4o+Pg0KhL2CS$ z-hZhX|I4C+|K}0?|3L-+=@I?j(L%h*(*RY^NEI6x^0OmIBS!D4V~T+GCshnGpueN1 z`q!{Bsy(0v)oU4~q8CWzgK070cb?aG`i~Zq1yHy7ESK8_;4{9GC&ud}kC{s3>ofGy ze>B0F39Qp5a#lyb7$jG!UiYOnPa3non80>@D4XidT0_uKyzDsQW(g0sADM2jr7oC( zSi^h)@uxE?lF0;{xucMCR~lbzD9KC5)&{8&_j@I|@(#qbsM9`L;TN8FJ=#;gf)cE4 z5^jCKys&f=e=k=SHFOZktb&)cW~>^?{wDl7+9Dd%%*6ws! z_XBTRq|di4u@tAnzTP+G762wB!?QeRW+yhZcozd&mHDjRzP(wx0Yk}`={BC&Ubivn zQis?ah;f!(#Qtk*+sNw>z{q)~v~b?7P>C-P!EvH_=Qp16f~mer0&#bEBRvgNY3&-EPQU8f2kz-@6HynYQaAjlJREBOvuIA;Lu& zob@}+EH&P^zm5~yv|ZAko;6mwk=iDiHi9XmCTdT&ZoRej&nSfcgUX1OrX^XQ=yz!6 zUhQ1yIg8vA{j#Z=rY_tYZVQY_4}n8z&NGuZc1;ZwN(2S6_&Q@s!*`q zJ9dIeyU9mw<(MOh7a_iDEW$g2?4E@_tT9SgMCuRVm^Q{@5yl?Yah`Kn_1MQGeZuDS ztoa;axhPkHEv}*lKc{4NmpYlTuU6Mf>wd`OrkRqIpYLU=$p-(Wq+!1c3GU(S;5+4< zmI>^@>YA&)B3-M1fqQtX{7&9mI|A=jcTt*x#GL**Z5R{Xw>U-@fZj2`42ypI4)_OX zZV0raPrHeqzkuLKi#`H!lmPmRRADky6Mm{CH62PXj$R!wwK$Q1uP3!iDRbd!B)h&8 zDB@|`LcPKzj30cy3&&JVbG{pG-HIA|2i!DL?W#N$GC~iz5K>l0xSOTYq=+)%r}Ih2*Q`HpNWFkTQp~ zxb&9-PB|*7^JbSej%Q25Oy;VN5K~BIKkFZeF~vX6(_M~1?S1Id)yh+cfse$KKr-TW zBD1&nhYfGQWm2niRa0OoBappYZY9fmTyD}!y^mi;)9Q9NF&Y5)fF5%4fY!MEx%5Iq zPptzZgNVCN&W$L%tWUU4MtM4I5tZBx%%qERNop zW~FWQ&Ax3y5QAf*e*45q`P8U|`=^D;1;t-4Jz9nn{2u+hktfLTV`BHPW} zH46I^ij@U>!_*?A0n|P03_{yyA^pV!B4YR1z5Y>nU)0-jG>SYixlR+1+3w0Z5mBRujEr)tYvkZl{iRLYoqdoM%8uEIJ$oR! z3z34Ih8NG&ET-JZ3fm7@ST=@q?$ z&ynp4y8SMk^a>D!c^$6=8h#@*P*{5Z+U_+tIQPE5-&(Dwo;3Vz(VbSgexD(xaDl{= zWcA2v@rc{64HB2J;%*k#26K>72`T!Ou#jxZ|3|hJwOj}n@rV&^D~6}33IXe^F8)I& z&XP?VmNHqXJwa^OItZs{zdfYt?-ILZzIsm%S9c~h%UvW|PZ{j8{N_70s@b?D5H3ku z*Y#9kV=gBgu@izXjTfP0^_XV&U+kJ}=9}T;%I7&|EAO0>C#a^@U2_WIcs>TJ*qb?H z%yPcUo+@Qks;p742#Bt6qVrcTPEPIsaLVujqTj-n+!Bt|r*Co@giSkPr8M+F>`p6p z;Tl<5{l$T#CKPJfA78RAhS!lF(L^yu?&ai z9s|G#UwubT)Wa8vy3p9pZf0Gq8dQO1@wU3_1Ed%_lzf`SK7+#G#1t5V#>L(MYV8Vc z4V+Qk1@fDJ(0h{iNjk{7xqC}>jOPqjM*tj#Uq)H7V(oEhPAW_Mv=%sJhQ))0a&JHn z$3lLm<#wnL)Ld#le9$w;T@1(5)z6gAe&ne>^qbzIBPuWKA#>Ej_3~xH+)~NV_98i| zFOq7;kz{H)HjgFzXY|RA)xF?wjA%VlpP%v-#{g3AR^Q5v(Xj{%9l-tt?xR!1j`k4K zSyZh-@0i>8{fYf-VOCpqcz-e&jQ=uUfG zVy}WN%#S9On2OSMgBf#zHetIp+vo=%z0b4k>u^o85O&l3I^s)8))cJ(zkx!s^fZU( zZ5fKw1!NTyyP`Lqu0?9F)SeEByFI)H4xMJljTA4Q6aBg>Q_(|XOQEWt#wj61f`Hx) zyp=>x6vl(V)J#vf`|~C{RvE)L*g|O&?g~f(o@swyjwf!5WVkGzMw80k=3kNnG#oX!@BjEhbg)K%L#k6+idG9GZY2n4`gdy+~yt`Aebxk0)O|^Gap^m*!&GljGsH&vhv>sq{tqhZ}cF*+c5Xeu%`Y zn_B}azQMC8H2Yj|+}Hq?qP(;V=hdV{4Zta`KF8lQOsrc>dnbAkuee zth~Q<5Ry%BhIfV5O0w^2b3Xm3w+g=a)!}maz=VGkc-8!->{LeyojQa?wz-I$czux3I58RW3914h>VVFaY$$PnsSE@Gz5m4H(@ZjFBb^LOt-~4==MpZawe6!9y+;EF)jIk5`fdqCm%LLYKZC5IdyuqGi>n2GR}@5D;BF|!hP)0!%vE|%Z@n#R z)>;=)D%H%a!m{X(-zVND-TP5+$#t$c^{fCIXu~x=cN${}lMNyvUe*<+S#o(2RfK0^ z#DRif$%J8V*OW72!Rd|IZsr5(d2G3V`fC23f>Mdm5n1#8nYH!)O}sFHB!o(I6EC6u z4LQ=Ay*6v8hMi-mqSOSdY7N_Wi}luP{=m-TVv23%2G(#{pwGw!d9qmL@TWxNH+N})cKg>gOsAt@)f)-%0 zfL?nq{}kU+mDuQ3F~a?r!ADaftr$%^L91mamoOrdad=l?nmgIL11PY%aIvc|R~L+1 zQ3hF@pT=bxecv;M9{clCVWR=B%Dk0B|P;6Ya0X7`YNeM7R{>iY^m` z0j@>N0|gdDfNLk+{B}knA8d_3-gg}$`0r>^Rur*w2Kj&da%PcUG|I@7hO}m^pGmuL}fE-zLxsy^NfC)h~?W8U#(lo$XL$WuO}j@7CCJ z!f%^&Q8{wWpsEo^QxUJ}&Rd-dr6Imb1+=50I@{GbTJk^m_0A(1Z!v9mFth&>zu!QR z&O!6gn+KP)T_&YD^B%XWMZ!FKkuIC>jcBIXlko~|I`aP5nI@cZTgT`a1!K7)f>%d{ zLHINzZZzSkAu>57%yqf5WWz%hekw)@hvP%VOE22+yRnu=O&`kBX=S*1A8WlLIz+6U z7Ao7e$m=`2R=WUwDZu^eoGRX`t=Ir`(ytO_^frU_sbZaZcP)ou>|kecXk=5$?S>(= z0+*T%-DuAvmXoYQ1*h_JBZFYF^L3)2Ja%}j$GKI6$IN727;*LHI1}aE?_fWP5;9Ho z;Wz8=$UF2P3;w2yI8weE(A!I+;i+B8AOT_1axL(9iM6CFHv0%btVDBNUCTwNENS)k zsGXRg!W@)fv=T4hFtITlCkHaJ)h0J8gO^-Zn~oRM?EH<_zY-hFe+LD8z*i=L31%_y zv^2cOD*F(-CVPetOdFUk;?>6m!ob_S6TGQmpnc{n;Ou3*8^DGTzilOQ-op_rKXT|^ z?w9n&^!V*O*;uNr-`E!02BnQB@UA9)QdR*2$OQmV>yCAA%y;vgE$y%{;7isceN#>2 z=>btVXvA)4cKOFk3H1xIrh{9pM+y;gmI+K|B7Ng+R6kA#_Gxji^2Uv(g_YhR9TspZ zf0bE&lg?K8{`{Cs09M!i=YCAUq9(f`r@cglehwVcPWU1tR1rqy^MGm2J?#nbg@TAp zH6UlB(?8mX?EkvChNHju*;cqG@|~O$A-;*_n-0D7D5}(0Z_dWwcrB<@$_BZ8;h<}O z#5>f=Hd$GwzQ)NF-=qlN3(9ZSpF=UB8}vrd0K2@)lJx=gt!Kliz_2!9Gu+zrdcITs zdpepg{@d(-Fqr#xk+EmOlD-Knl11-_k{kurgC zS2D8p>bV*z)da_cTU*Fl&^tu?>E<3cm|$v>dvA;iCeZ*>citeN{Xnu6|3fq;#f(wU z@C~ECvTD!NeAjHqFr-m7x~9eCY}b?B&2Cs(mwFSRc8{MQ2Ptt67_Z)bm!j6*LkWc1 z7eJf@w#{S75I%a;?MY(>KSMQ}BZ;c^0GP=LqA5&80lp#rl-K)&ivGqfgZB4RM@IhO zfXA4k>y=>pC5B*|wP{d@^Jm(ONb~DwJ@p$u37IDkO>xA|SYjPhkoP22LR*arNF?g( zFU+}IIIVE0zz~$PiK);}uhNu4HskLUF}4+1Qm@0c2tIc2Kp#=5*hVu>n+nu8EyzD- z;RWOSSC(^>d_Ysk)q?R^^&PC4=CH~gg*Md3EH8lcU|31zF9`nilHR*eY^nA5kNxCTh;v?V17MWeBR{mXq<(j$TtHx~g92tk0t9-ZJ zE=i0AcKOt0Pd$n57RaQGGGlsYZ?$}VGm=!26y7WBxtjf54ykTl>}uVXx$1sVcZVE! z0-@rHT(wQtT_~mP_JjSu&WW56u%FxfiINxAU+I!Z27G8&>*y>6Tw*%+DEami-Ph-> zooo=3L33U6Y`%9an9BvLh4H)nj#z&Qq5PgHb>ZjUX2{@S)QCZS@v((UtaSP&27z$~ zAvsdKTf)p1Ud|J(ZbWdcPSvHZ72h(um+n(*;Z)#VVRl%St^e&`fl#OQjJa! zPcRy!4HJCv8vXP>shVu`(&~tCTPH{!!NdT0*ijb56GE-wb!C~= z(}59#47ttkThPgk(D4*pLEt%;_4G);gmO+Wd9}s`pJiNGaNW8iD5fs1v|acn9hCzW z6&TqbN#EUmq#iA@W5kYk>pj`|OMT_9@k}r$=9!23V74x9Gou&(2SNDXv@B-C-jaCl zSw-DZ7u|?;-}FR?Cd_^m^jFyi{L_?SLmYQt^t&pOkcu<}X8BkGb6;60(n^;VqvV_xJ3?$hhf;ks!EKW^yz*YdqwDkrtNpBediy4N zO1RBBnIznm1OBa~^aEDk|EEgI1vG50aba#JYIp5R$&@JB=<5n?Rr3QNS6ut?9^kK( zIx%qk3jxBvKoko4A3d0zB&KBRhHj6#)o2(c8eH5Zw!V@p_aBJna1fz`TB!y>D42hy zD8uf;Sw|LqHwQ22Nr*C!=BhR>S@F~c)wO#>BHF?F{~lb3#+l}oC_!ld6_f@5fZxJ@ zU;UR#^50pDDJS@!IA?PpGM<=uYP~`CA>DP$0^=uVa|Dz&&>l8C_r)Yzg2(ifm|;DJ zirPN`YWGKW$=%OvnYjPIb2h&$7%?1y9#Beebmj@*&5hd~=29%y;${ zA`e8i%c-FmDUYVSvOICAbBxRk_VO+7HSO|G=sj2}y-W2yd zMi7J^lie9J(|K)gIpu`MIpyjeRT-1CRL<4mS~UL{8T;7j$NDo%+tq#WtYQnmrcvn` z+WQE`mmhQIAbvf^qe{>>JPzEL@bN5>E+WZ1qrp!%UEec*4g|`)(evHo`u%a2wi)G(HX7?|%feoL-aCD}MLaq?Fybk;(@vJGS$D?QtnQE|$|S42 z^opm}mBNgj*n0i!p7cJLMLq~YkS4b@2EE(#{eY>5_Nd8V4a-?76iYGgjg|9+trBP@ zzyG(^Ql8q$A2h&xuWTW8eg0Q^L8~p+1?)`T8&2|p;8fI!DC5!+b^E8`oLoo5s)*;y zN`DXLS-XUAe_Y3}=u3;HalrK>_)b9sz8q=0K1<8a@qU)udtDx!j3arDslo zlNN`rwXY{&UvyzIB-`l>d;%Q#(BSB$uUz|( zR@G@fNg;S$AM?~{dFC&xBRsr#3n;#xE(#LoB zyVG9=i7mrmwA5AJq-FPhurd& zJOwJRja*juv+Lc#6xt7L_&I1Cj{oN2t448n=YaVIfYD%wQC9J2G&@_c6AZxmQ9)N^ z%I4a}!0QQ@&z_G0&BY1O=ylRhEA4v8UcxL0i{h62$S5J)JDo z)8L%LJ-BBg;`NcPEn|O^3*d`Mx_6Lyc15lr&f4F7kpZkBe?`e4!R$%oM-Ds!lUl^X zJXm?T^VUK>l9R=quLEqSZg@&U);RBY8{ZGNK0$Nl^irF(@6Nb0NI;(RW5c)K8um$& zEOK3c@e7Itezo=vgfU7OwTS@{W(0H6p9>!gQ@cu5BeI}(5pk6~&qeKjRCi0k==C%q zA%GyyzLcD~uRz6)=53O=9OIiM1&eE>HA=CU^+X#3O4sYbcDif7q;~3}fYnb`)mD*EY%fR%8`*ah!X;k6CGMx(CGI3=$HH4n74p);`@T<9M z#6dvXF)g53BF$&LM_v<(4YHVvAd>tNMoqpQgn+)oke0%d&&B+W+yF?ki=ShOxm=GG zJ&OoxUIlMG@x@j-dcsGuztR$tlF#M9^0?Xngu8&E#_)}oFW~$8lj5W3iSqlqFtad( zqi!*SL3V~y6g6pixW_xW4Bsb{ZhF(iV0jKq+O5UmoshN!)-#(4rX=Y`gwODk2mbFZ zahzoT=;s;3r>WM|E5bu}rcF~(alW#QB#WS3hC(^|rntIA?`p=EB;q?fOX@#Z-ue zKn~zL9sFuBuHLfJv=`QyDv_-wg$i8ER3BL!m(pOxqB#wa;KPKmh; z=KLX}IPV?3a+LI%Rf6WyHh0D}wGxxKIa(QY>`Y5mh{}PK3Ms^+y_R!OKcHdCMu^uy zlkp0=vcBpeQggcP7`OqyVi-z41lPMYx7#ZlzF`hY%|zUV`PwSDavtinx~scQNwSDU zWv9P?f0yy=I^uo(9IlXj$+4_xGd^xXSWF!hM!73&zZ&^PzgGIMr9mm^Dm?I3eCd!x zFXPFZstFrMLa+TwF*XS1HkRz;zg+Q$QZF9aZXvBY*uh9`eo`Zy~9H9TBZwvVkXLde*m(a z5xFzIS~{FR&jB#vf-i%cW*g7px(05CDuhbRyo16?N7=mX+Skx}t`6Ps9AgH9%-CAy zhS*g4N#YDpp4GeJARwG3yW_L^`*Uwb0~u*1B_`!T41(4`YEw?g6qa)90MGibSG@Z4 zhtkwDL2qf`aF5#?u z+~S1nfFEmC-Y{+D7q&Bwh0GRL`$kN`%E0mH?2Tb`qx_hSZ6I_846FB8z`G=8_mj(S z+7?Ss3lT;xs^4^3cjQTRv)UprtSu}9@Jamlu%#>?L<$v{U;$ryD+T+@%+Ei-EGINZ zibn|~w71k36Z2^CRkSoU51|k`M05|%SA8?sH7+jOBWLb86*+f~o@K#X1qb=d4xulo zwMfdX&E=o zohht6QqB9279Ih`Vcqbd2YD%!d^wFH%;+eyZDp+`(a_~L1zvjmV4*p<1dtlD_HkpP$@t^3QsK~YO=8hlnU`I&J4z0ofzSrwNKuBv-)gwHv7(8D zyuxq+H#bv_f<>_>b0|%4FN)?ESDBIf6I8VJUP^L-;o0?gwPMa>gApnZ#nz!BCnhbx zk$(zX)!5p-DR=`Id2kud)G)v|3QM`_K|n0>xyQzN$QmOFf$`ik2v|(UTzsVxi;ICWsC2 zEr)4aNcG?Igkwn(@4l3f=3nxu0SqZeIR;D?AUpViQ=IekfIoHJLK3coz1^=FqYEb2 zQZFs4fjg?%sH;P>9zi4Ls?kXOl}9v^UyNbE;2wMzpgUoo-m|Bpl>Pa9aSHeq+aG6h z)+w>>@g+5HAm5c!_*M&?FPqKv0vP`%3;%`)XRv}8N2E`vVAJ1mVXS#rShyyh| z`GmO8U~9*NiIN{U7F0Tpzl&`2FKXJ18g9pt#72mP#n**P*;jECY){Rz+qA=t<|EJ` znobs10dT3>#AEhb(>a+rrLqr>I?6BD_st7!p{X2NVj!>dGgu2)w0ej63PIj8a#4y* zldT<`4jd()O_U8{Dz9qX${!snNvrDE;GWQ6xgtW&$isbe!Y6J}Oy}|F=Z8p!#+tz6 zQ`oKtTEI%8=AWjb%;;vVgOV{HwncAYLX+WtZBITDVkGfArdkVt;;>XU!My?coRoY| z*||}2@`M>WlbT$|!A))B@n}~Cg6)a3#gZy!5H~1D;)BcC`|p9RHlN+un3WkG|tHgNfEniRJ5!EdrKY_vvSC?t()G}i5OR# zuh~>1=o|F4^f1qj(nPIsk|PNM?eyx*i)pMb)IpJHYBE>c{?dBOI&ZXS;XL-xHw8Qp zNm{sXaz-Ba6)^5JUG@G|UQ(;0Na3Qo5-*i#C&ACTgbkwZM1h6(Isq@gag`?+@Gu#)OU;&^a-E}@1r8=>|qwT#iPUDx2-lbyihT!Ycns$qKCgY6Xs2Py}i9IPAl68vJ zhF4G){-%Ni^NGX@TMrZwA`R$>=A}=0`P0fo+LgX}19F}yWsYwH&SUDPrDmi{fzQxt+C}-ArhQ(e>KKz9$!4WA#&3Dnv90 z<+2j*UEq5I3(0)d5Rhd#X~l0y=)=flr0Wx@g!P7ppHpm7dWVcrpoZkSwbx)~jQMk1 z!bU%Rki^b8;#O|rbkfv9e*Bp!Rim}6PFB82)iiMrXybKH%K(P zr%jIHJcV@B4kr`6V_W#?v|{RAR~1)lj=58ukk$l1UWsV5#cLbGwdaRJ(wMPZ2Q>o8 zZY-aS2cF5DG6pJaC^z0h6OqhrxrGUtBnF2&c(T@fup)TinUq_}xRjTWWHUKVqr!ePLoQv1|8OJEa+>9tC(GTFhoT9%6>6J%H-%GPT!0IWpBeA%tU+nlNfr9kzR4lVD z#HG`d4CWwBqJy=^D*~9(L~$m5O=JwR$E{p{QbFlAq9QWm-S9~en$e+sAzFi8k1HZh z;&_4|XOvB0WaIjsjw&CZ-^vP`jef>7KmKkF^Z#dmcbN<~6+o-fI=rYWTq2hCicdYc z1=%LPM!txU-B1RM%HOl=cQvD6b37R2mI>7p*7`X$ zj_NvgBfc+S0J7;F?k-NrgR7LsEdb&aB6PBf)#P7qw@c1Wpz}LSQNG2%O1`Mg4-6!zQ3Bbq~ajlno?Pm&qAwqQ{TFr%=*GwZ8ON3 zMm13gt0am+>=un3ijg?2F2FNafc9U5wQ51E=nzEOt0clG4@gipRexcK)a&>6_F5P^)nSt*VA2d!{qxc~#yJ_sT#!D&?jFH$Y+hw!- z{o`*^=;#=WmCwT`v86%9PbE+c@w1Gvh5xbp-z;P3Q_)y9YVg$mtG}C;U7|M_$xwTb zFnN6!r-49`2qn*=f}{~^TBih1VpSsuah=uPQufJBaSKYdNd@Mg4gZ!J2Q{l*i?~Vz zGp2N-9QI&1Unrwe`v`W!70enGZ{0&05!=1!fgUQOXSD2jx57^4fh8(eQ`Kuygar3c z+me@cXw_jYds##T3c-6m;+{2_6v`Ui!=+1!7t4&$ zzC>pM!#%~(9N_TxSJMFNk~av)#>e1iYK)sb*LbcBW1H(6vr&$DAfQq+4qy5Sj@852 z4OAb6s=?Oy=C0`1;$Et=F<@fWFEU35OvEgN$`_dh&Rz1btGK>I#FePsKLFRDxm*C< zr%>6GAvTukMC)Q(3As1Fu};dT&ClvCI-Ec!X6=)gti--EiGPnAIx6m zM_z*{d~ee^SFcsf7Ek`kxrU@i2=yVErm7$X*}v5T@D(Y6(s(%RsfvNFSbS0tKwd#+ zf;G#3^ibwz%E93pVCLY@#Sg1)(XZ>?65L-f5INv`3}s3WS%b3J!c5p!~5b^^Q& zuk#rt)rzPXa}~;WMLg#g0k);BVOHg7Gh%OV#&)5!80>e002h{>2HCnrUv3V`)iDh$ zH;8?5x`ct*m-$ITzjuwhIWh%3mJWNIIW}d3kp218V7RQX%xks42C(}(@`Q~SJBI9~ z<>5VB8sV?Dp|kTEG(1&cQI2#u-TEE}dTm=1d9?mEw`I;1R5GjD-91=W}$6a`qG+E~KU`TDbCUN5Yk)Y_5R%^hsB-BhSb5CaHZFO3>Zh2+%BI6l~(NB3z z$%HRu1#_0{VU06;8$AM5FCX7+S>6k%%t?7X0Glj5%>oedIkY zIuUEiepA|p>&{EH^-0ErP3Ve>?1MiFp*JFZhi!NDNw9OADKVIs2<%^#(odf%kOAt*) zu4Cy5>Y1|IFK6(M3G0uuloupSMn{>W zu`m5<)-3;fb{TxWUuD(1K3+r;>AFz|AR3!tEzUK&15w;+(?)c3)y0Th9P83OFFY0k z&fTL|nK==ubXPjY8Ro>WZ1*ci^qu@Q-;#yp)poDEHVq|lfO0_xEt#37pFm6)4xeRW zf*_0!f{z$g(^Py*t1${36T!;(5j-!bS-N#T>A931gJkq1P#dQ;+%G{Q`T48DjeH}fxLR)HM!4`d0i@9~5cpA8bk z=u@Jos^XgOo?+GLT)6<*i`HfLsm7PVyc9=FkBA+C{iU?K5PVv15G@pSGs5J|(leM; zI2lT%o*u!$S_=s37XWpK@#v`#1t*}cQBz9~>A9y;L9>ZhQE@#f28{N$azK3X2ik$d=l9>l))IZ-&F+hZ!VQpmI4I*u;XYMcYJuN1?$2yLpX%;T-fl+LW#rSeF< zu}y#}KD6OubHu~YI|0Z@d8!gE&D%5%nHD%A%w;*9gtEw>r9tY@)ez!!d75NmXJqY{ zW^HL3`eYy3_PrY9>aX?_0_Nk;2);;{3*SAYi?L`E3a4j>d_m(_eL_w)n$WVtd6IG~~u zy7$56l-5>k-v69roYh6)Cs5wcAkIxdZ>ahoO)?sv{I`>gTosT1G0E8bv5P5S;NZ9f zUh#h|$=HU&r^W=mI`vJp@s<&iI7KH-a`IMn@_#1D__@&g-$^p+DRalaBV~-4RMoD1 zj_{cH#ai7$7h5q*`(7TALI#UL8-VoZ&Gy!@7v3*5p`pqWh8J}k3#BNtoob^l4Pf*hCQ zrlvJ`6FV^OxYX>HTub{ZI??wb^a@1jATK>08Y{k^qVsBr#ChpGj~c#tu#P1)916Yq znVNZ7bIg^{AIni^V9oVaBm=lF?*oKur$PhN4J6jNgAeT`Xx!M8R6oTzRUE16tfmoi zHxLJDnjzo-Vf9T77jD`1F`SaTR?z-ArCa0D_oEMnRo-vaQw#B~>yOUAsK;UYQVl;V z_`T@ZW4yvpe&>1HEcb~$y^GV$%e_xn5j<}?DSj&@FIzO1%3&TBg5Gq<>nykq>>}91 z0-mEcVMmxu^2GeGob zQoK-O$^_3MUq79G`8<#ZLR*xrP~PRw;!!S%?el~r+WPMaZ^un`FNeq)sFu!j)WyUTM4s@RHPY#KZPwuT2)cWJDV6@mem+rJaX_yD6Q=43=-Dhq~5=|J%apW%~t$}Uv z6X0-5CLHqwB9YW=6EpqQ%pM*Lb5)W;>bK6S>U6Czx$xTBu$a4+Wf+i7N(jd$8>#l? zDB&NixJgv8phcHSAM|cEcS9fp*kqSIk`QugoNT!5lEBy5pQjNB`!Q_N*B5paWHbCo z4v?&L2Yv$jJ<4X-=WU0=!l1mJudm9iJY0(uU~(i$u_;^yGlWj=iM2+m<8sg*7E4|e zLBw=Sx*>;%FXSm`lLhr=bAw`1i_boV=G;Sp;4*+%$;AG@v!flbuH%1oc4#WLWd&{d zXq9dA-dF4AEkQO(CET+0Z#Dbi{1y+%1K)B-@2z$Wl2(ykWI&02q;w*K1|DE>I8_gj zprsH*zyptF64gU{BqH{?hBdjMn>gfxJzQ5Q%K#Wi_JAh-+rw;XFIP5!=Tv-O zgGt0ri=$gk%IUIX1?tM@!q&g@VaS+iL~q%=9>sNU?I&QAG1OxBkDoO4Avc&V5xwyZ zjt28DmiBU8ZX9IvfydQ}jh@(5>FD`RNX%%bI)qWc$7k@|Jr&!{imFra@SA*`G`jzG zR!c{=5BEQY-A|V)>t{hTm4gTV?`5?%07g7`L)(SxtZwS}B(MTRk9N18JcH&s?3?$7 z=Q~?j1##wL?r? zkKS68FW~1*=N0q?as3HD1|#t!;stE@I9JqX7X<})ju@DL_$3(royaGyh@$A{npf@}?6?_T_>>H?2iht`aHY~Emc5bxwxc?# z6}jRNda%On$_QtGltJ6g%oXn@*0OKA+uMMOf?@=zUGW0qC#p=-D?1K7mLQrp=g$JPE8tkU3NK`v=PlCEP_S z7Lx>p$Yvi|%oo ztFgmUe-D#Yjz_JM`}XiRr+AH5zEzV0gzr2w#edVHFFZG*cFD9hI;%Yt=?TQgjN3vj z*T7V#UY3uhiFQ2AoNE0Tg~=!fGo!vZ45s5=rM=3%HG6w>R>uG>MMrRWg zij^PG3R4dgZ}sX;JzEd@#9R*wJGlX4Jan$tcKL;l0L#?=ZtFx9; z)KE+Iw6nyKJ6aKlTp7^`8^6Xu*hBX&!~_)pI3Ek@X)d0UntD|Tk0emv*i9VK=~_|b zK(PEzL*U=JQOL{N@uo| zc#?aq(Lv-keh(wrgnm!;fgm-We2BRtJ9my`q^*{x%yhVE)|D0%PZ-14K7BO{!421P zBy`lch!&I{u@%6ruw@CylL`93;y(SRaCzp6A<* z7F^Y#nN{b>ZQqJDCZ3a0rGP(Kwfn7(^;${%pDs=cW%c{SJ0>>sF=;Y{|0b@ve5r2GUB zoR2a6G&L&9Cz0SI43G*Pa%*GrG6HpW@ue=7STpBMco00yGQ#P2sb(40uO0pgBZT!1 zLOKnWoIpbOK<+D+UF`P3en@h8A)4j@OgFKu(O*GTUHRKOtrdUp!_eb$)r{+1f93SY zV+P+bVnI6vEE6_vh)=jixMO_8En~<=!~iR0r2Mu{=s}uZ%Uqewd3b5fkm&;C4;s## zjJ9ElN>nKy&*C|#Gn@0d-o8`bs*!3UPGS*SF5mJ{wBOtn2+>DHsmcHiCL!ppl7^?H z_Kk050Im~nGPlOQQUclF>z#?J=`@aXq6NES-sc)n$(G?C zV@W4RK^L33UB}&L^k{7Mp+p$a<^n3altK=1JP%KI#M5OtH-rwTnB#k)m?j?hYwA}jwkj}XkXIIoNROb9Vd z79$g>1%^JrqJhpv9Me_x7Ri+Jnp0ER5PERP;=vd?#%6P{441w{mWJMyO%W&=*_Ji- zmI1aQuQN>_zbM!)Gf|I=jVI#?lBWiXdiN_)C9MDcAwVg^tc3PS_jUUBl6NdM>(|9J zMK5Ocmg@W4v7fPv4*XG4c!Dq1;%*w{~xrD?sAkRK9{FDi%W4<*lPb zJ8)tqMFBMtEsA**l;IJUxy!dW_wwOiOQsriPk+^0pW;u!2@*UNI_qXR9c}|WC8s_XinO*6 z`as=k>5^_&J<2mHQX{+kym5Sk!p9jp->-dD6t(F~!1Fhm`YJI+ras?=p8&Gl6R+(< za5BXfpL0)UG9N}XQgM;P1>^e7Q=Bc(b#LQ=*)QPBM-LS3Zoub zr5~R09@y+dy-jG2y6RLsZz6#O8f8t|M9F)uNHK8h!Q6%&=YKwAr&*aN`AV8u3M$1}l^$${rz>TaX}S&Z z)MpPi9st_}iFsU-IDH6o`$-liAyUntHRr^%k{{D#OOu?t?#lzJh?VHGKSzGL)&_CD z>5w|fr-4i!brMc^{Q86<;Qu%{Q9?7jr%O*->%koyj4lV%N=6XP zg-lCsGf>B`juMp2`!Vt$Bn)tUtRnA(@O{Yyd)&}Y2chtb_4h?=j?z?zJYBF0bYGs| zQ2M_17)vXqylVrYyx>=JL({P!ESo|NliYWWjzrN=nU(!`GWySqAe*z zhUZf%u>Y37BVbC@JLW6stp%dlWF4H02X+iCo^rbj{92MFv)E2u8jh-UoyqU4>v=N?_+>y9Vj)BoJKR@#_$qty)mi6Cc#hj$r-f#;ZV0^Ci)$6pqZ8uo z;oxiEH9!v(Bd~yoxlTam2M29W5{KP*2dn-|IavuCAuW?wIZ3B=_r-TsRZqVTf~ z_o_dniG>MEV@zCxc+a2}CYi%{$0cSo&9}?I0Rd(R%TvUj$lK=m6ARn6K~>KTx*9X= zBrjXJc~~cfCW9^NE*7&dwmK0Ht-(F{-DB9|99QWm1H7xz`7+sfxQ6IbAy&j7iLHaa z&-KH~#A+Nuz^*|a8WqeT^XubB-}hAptYD2>XV@r^z!R}z=Zq6X+NUGE_FKM$0w$|| zKC|T^hJLUP`sWBW4_;Q&_ag0OAZ9*HU`X_q@RQc^~TdaG1i4o0((Oac9~fN2}} zu=jGOFWBu^%)VSA&zQ)G%P2m;RtbjF)xov6Je%9DnGEZAJsgeVt1~FyFbph|_Y>Vt zwKpC5%^D*q5Zr8@8d_J#B1m{R<%8*Umiz8BaW84QG>g4ZFz$3Vzv-@V+b|pjMsn?H zsiT@}P5QZ0$Zvrc!Iv^*10Vb}JzWxsBuJ*pXS-uhph|1F9@-2lKrbi2Z=#m2#Pq`z zE1vj&(-ual>Y1`NHAoTN6S=(-p;OFjz_rb{bi&XKjJNYFms4riY}5;)1W^qZGwCSL zZNMU9NlxYD!+FdAYbb@|mpi%K9BlGF#sd*64yEz$IyfgcKmiJ-NO>J-#)5n+8ps# zK2)*4uwCMPa24~D(Wbl?JyNEtvNts~UBt2;sRY>v6*%8lhb##sW(K#uYbfE`8KDca zo1Wf>u(F4?FGb{Ukyu2CHthiIDjCx+J&7PH`n5>)_RcpG56-`=RZcPcZP%zhv!%c+ zS)3c%So%C$dy@AqaF3GdN42}-r|awtrHMPHwETA%*a?NTMi%-KY0}1V*)L1ZfHaC- z>!SDK0XXd~3GGDv(UQO?*Cterk6eY-h&@FfK{aXrAvS1E&tuBP4#9d)^RfhO4)$Z9 z+j@^k3bV*xMZ|A|)fo1Cv z-l$zc2eCEH+-9iLYS)0K%!+DDZ@UvcH?=sKoM786jQ-wZZT8-mTes+}z_6q6OXmjV zawpCz$O=A)sDjg*t!LkXi>0Y^H9xb42x`=>jSvkK2Y`n+$V{v0LXfWN2f~AcdqHrS zSX7F+dB_E`;;g4QGFzmluFr=B1Y+j}-#jI^Th%`p-bXReHSf^VO`t&~!&yuZSJN#Q z8pi6%K6~`^-03yyOu3oz?7K>Jm2aO$YMAY}`5F@q2{2BSpd$IxpJ7HP6WcEP{A8|~ z^AHBwV7J!rQ6SJOk)bHVsx7#|@`83=#44Jv{2F$ZPo6wSyMU)pVoboizPeXuDpG90diY zD!B*M){`D#tm+m#vQRlg?FDS`=s@@wu-NOq1!L&sS#gYzN2~pAF?NrqiI8^O5NkV| z`~edW{HEwGNCo=!)H`@|cST}zjX?I9DoLk}j)Wm|nf(VZMW4RVawEM1zqwV&dC47S zDOdY)$ZJl|aZt5~59;gx%Z*32V&1L8O=iU|^5*^$5@rcyr;H#XXSys0! zLHQ>72Q(p1DG7@YN-yIxhq1X%Zk#j%RtI7a-K0KnJlD1bmuq>>hi%S>+kkxtc`uCU z#PB7odgWPDB*qSAGW*;ddnDWPHLjb!tK>^E^Los1{1b{QAaRe^U>}dGeIQe| z^bkf^3s;Y{w+39kmaPjM%I!SnrFP_+MVW%C4c9^M_6@Lnt~64yIM@2k|2)0y;X|0hjJa#&PlBexa%7t@GHvk|6f}k+`DRHBVAI%EnUayc|eaZdI zZVNU5L{w8NxniOeD}GiAb=x~3Qb;uv#253d$m;7Wc7dd@kg^G%%p`bGO#Qv?Ai~x< z)lIrv@zv$_Cpknn!TYYYaP($)T-Vhpr?GZ15@y|zO=sRhJX5DALZXp2s@OO^aK$oO$Q&W@sU~UgwSeuI3`bXb}-%$XET`Db=U@*J-TFWW3{? zFFN`SmB2e+)>cVHw73VEW@bF5RJ$&-6OcUMx6vbX;k^ZzrO~)P?0Co14mEAIz_1Q? z3);DL3$%|l??-z#uIVrn_r)%G!j^jFcI~?)?YaMiCd7zxy{9bBL z3fHf`8iN*Qz-DbmhPoDGff*Odvko(oV9=S?BC>mCWHOP{meW@TGkS`-0(Ye{tM9GV z{$sVEa~ii+2W<{egUoIFZj)_{9IFflaJ;UkreTv*1&n*x*g84MW7(g5k^!|i;;(Joy~au@C*gdGFwuK5kjDO~mn>T$ilqGE2JAJTjsTZhfN@#* zZX?>7&53Y0>v%DJ`X1kTY1i|Tg%Hm8jslKx7U}l(CEO0=X#&Jbv?U;V4QkhHW7t-@ z{h`qScPrTMXG`*7ZHbm0-3yU4CO;xQEN65Nt8N|{Z1FHgU9D7Gxe(V4%|6p`pPlt0 z5%R!LHT^^%>*5WcYUj#ZUzuiPz`(EGn+Tt!vB7NGG3NkG90gJlEng|MglusRuXBkuc{kd4G}A>mYtwOcj!SDYJ+&z$^)9rjsCBl9FT%M!_D=Ct~Z^o+-mb<+8HN8X8 zK-51@Dyr1}v7uwV5~1PR1T$q-H+S3|zCTmlqQ#vz%r41@U_mP(jPBE@O*3V-)8tu#T zL>@R1Q|&(KhcI2J7uvG4#t=e85$!^uzMY*q;;u?D6N98D4dO!m+ZH&L%a!lh6YU7B z5KHsbm$okn@tNi~ocJ9l6N#xi8W}hC1m;+A)n&YmA+};Y z?#m)Fe{}yA&+@y@Y~BnMOI(XbhJW-ZzKe8bb?sb(%}6Q%+;+a{z$MZY87cH_nx-)K zH6IZV-A_$p>!-iS{){Y*UsB_PzkEBmK>rPZ!(u^YY7?Do5^quPD=wK=>o(%eY=kWJ zR`Vaz_dD{getwD1*}V4AK}nVdL9Cxo4gP8$PI&SCvXdy!-44jI_UkADSlle>kI&!f_Xz= zKPV2!s7Lq}6d8A(?DER^-jm=@`ws!En>4VXFvBSj5Mt_y7^ZMF6IRqw6mocS9}9jw zv|_BK6nIp+v*jS$;bRWpWRQW~@VlhTLT`_uv+JH5A1)p9rYcJY2!X!b97xHJz@XJM z7WQbkhrq~MqC8Ehhm zSm0BCwosi_-pyCO%oo+SnBVelWy|Mu@!hHP`u!Dx4}p|Trj4JJtamC$W#Pgrm`_KOOqlR3fYM7$zZzQsXS-0(n6oJ}UKc)en}3tr zO)sZHoT|mbSr`tLXlVlJW5V?8D$rvY%&^Aoc2^g+z@BY&+6ek{;DtxTg0aC~-QWg` z*bD0Di8Y4A=d_2&CW1n~h_ZS4pqQxDvgPbVXj-XcmcHnh!s2;*d_jL&5r5oPu!Bj) zsr2~gF)sfSz*ZIpcM8sgJ$Dz;pj*E(OrKPXWLG{bRv(xe0u%Lr-`D_tyXOQ(?LnBv zZi6vweFf&9c!_yq@6o<7ChHk1ZW1BMyorP zE-!!a_w=K7h#@!GOsE#V z=Q}2Nb+2c*m~X)Qar2>lQkV#Jj%k_8aCaZ^FAg;%O=a zkV(G>L(M>MOzaS?P3EBO#&GI+i}u{A1g8!vJS&l! zV^cA-Ih5u3M+6t7!X`OR0YN&!a<3zPj@u_iWPTv7F>1VuQnR^MKjN)PVjJJx&wU*~ zA2IQc4Py%K-U|C^{2(>{y19DMzoeMpdv>o)GW_k4&+Dnh;%~hp zfzy;Q47w{v*{L4Pjn6G=WD?eP4=k0^|4@0?dKz);h=P91j4>GiJeWdfY=aK#U#j-n zJ2!?7w|SJ?cv}TizDt|Q7o_&}OB3X+whoyozvUOc46G@M znJ$&x>v>6M=sfPu-^cfcqPki!%?;Q`o|LooDUg6tAa%ZtNNS|fz?0Y037H;l!5N9q z;ET?ObiIK`=@Dtn7AL*~{~UDdM_nDpwy;#V`XcFf%JAi8bfB*Kx=FpjrpnyZO}nCp z=$$HDsWWGQ*2)D5;gWHwmn+GL@40{hKSBWZwU{y9)!Jht1p%|C7`BmNk(O@7kh8Sn zMX-)17wy>~=0g~@rGaeu;=cuh@m52PPEX+%6|*dox-X+gJ{&`pBoj7n5yM%J|3=b2 zDp!vLI$iL@(T7pr3qL|S&{r=)lRyihn1>Ef+S%BXAk62#6)uC-hjKf7g{ejlpY#sRkNT+nuQf7Pvz@m>VO%2XxRjv z1X2WGHOn!ZyAjQK_LHuo-lLcOeB3d3wZ|oN%;5*R7xKfoV9I%1mx41a;26>>PIP)+ zS-H>5Qn4xIDR-mXxr9bTs@lFN9UQ~3K{hj<6oz??6MRT;vqxoxu9_*Dq$SHj7Qe~C z?pG(VB5s}lr_*lRW!&Ghcd#x7=v&co$NN*y^>f}!W>^SDDV#Seqt}mNrqUgCPn@kl za=f0GC6=q+kMqYrc^Ms#E@gaU92qC*r3^b6K-K+JZ6Nt_eFQ&WTnhJxBxXo(`A169 zD?6Tg=@si8z{6JA2#9FM0;w(gaSwGox~+4`o0ctaxjDU-=mus{1d<#1KXl^H%x#PC7uWW*d=_#?PHdIRO(HsSQho z2j}ig{w|1I)B1TVPE6QDu;eUekxpjuy5{p_xKjr|pFa{h?c&pxlA3(d8XZc(WqR#b zjqg=aGsNzsZic^clO^A$;PuY-m;}r42Cc=6Wr=l=Rg61)^IWycV}#E;n+G3&c7%ER zN*aBRUpB}(SA9NtDKhA80&3dUj~^s(`$2)Iu{qjoca1y~cQ&Z_<&*k7949QbtY?-} z!`00T3ZL)&CIMC#@d~;-j~UHd<*ORv!oMgv!3@Oru2JP)y`6S0eDWnNZh;7$3Qi&; zr_j$&d>qr1%3vcYzl|rZme5ReP&gLjWI*1}ToHojDU<;o*^1jX?sp_5Pf|TH$bm%D zw;`>9sGKXx?$YhP_mQnxgzonu0;Fww+33n}zR^ASAz_iX;mi7Z#=!z{aVXmPmkXqI2aiO zn=ko>%m~VsyyZkqnQOf>!Rz5hk`5e1NMT3Y#mCtw+j0rHafo#k@1Ha^zq|FBX*l{w zD3{zK#_5(O6Qg=zj7GYo2GkX>XHHk?+EB8;hkMOw5T>=pLbzE(i`S-X;9D!3<)5ob zx7PX8Z;i$Mc}w&v>d~XB&VIzNnrn$Nn}w}GxJ2=g>DIaSv2+kqoDA|6tTzmO&`>k< z_9@$w!J%*q!TQj6V8#dV+Twi<2)O&fSwbd##3I0KLObli6b5}Vm1;8Meq|R@CUTV0@c{? z`ksaI4M(9Tl1!Nv6m_McKF~1@O4l1=@5!w(ytz21i4N`fE|Xuvmx!&j6fvT-i*ZdW z>TNCVG(mInwX8JvAS{3ZwPOl0P?$+OrED7c1lmt~qi#k)A@@?jL30rv;b_onG6Jp7 zV^WvPJ~qBZf@UjS_W2Ik;G!2p9lhb^f4%pBzrJjIWfj@NWpAR=Rz%NR+Mrmj`^Jpq z@~V-~S-+O=?BZuSEVuFhBFk$H`REF2<;RiDn#fl$+@X=H;=%63$<-mrbEf~hqjp8e z)cL_wjDq6J{FkB(4WA7G9wO!mSdkunjHG=RU#$ZR1)!VAk!OX4ajOly&KKOX(>-ox zU(=JW*Eb<+&HQD#+a@TXmPnpk1JA^cG5^VGvW4B#OZ)j9QVwyPkNs=ngIq@wlLUEH zci>?|RWHuiY`f@O;`uSN9|giafPA0!bLJZ~fzq;B&A8m^$|nG3!S5U}VfCSR-WVuO zEs0q54sG*KhQyZX@v7Hq$W@;+B*Khz?k9( zCLS;RW~a9>Ab#Y;qV*WNTDO%#kv}nxJlwF3S?g~piM>tPJ50bqeX@>|@Hf(RmPt(l zCRjhh2$8eDbMSj60Q{wM!M`ql_>ep2)O4Q1o zl-t9c75wJ?VEz11Kw8Oy#1%<@$*z@Sj38fCYm1f;r0pv($~PW0pMjeko}tk?1x&Zg zLRw{F_ejCz(48h$Qo?TFr?=3ree8NQV`o@wjxFF5vCcZK2KD=Va7cnm|0%?d7x5{Z{w;`OKche?iR^McVy&Vj>qPVA5cpas6zITLAu&-kfn&I$Cfj3#>31q`ceXZl_vN3Oh8+e(Pm~8qjhLw#I6~zwY)w+bMT~lm z4MeWz{1J|)m&HTE2rw0k0NdTx1pw*Fks6@P-V`ogiq`_EgaIpP zbK$j!q>h)WbV;o2V)^hB8pEi8Z?C&_0WGk=F+KB(EhX~0`SBm~!)NrN=Q@s!#3Q`r zF~GFV-bB))0$+E{W8zU89*oXciBR;9p&V}& zcQdg&T5x~@iS{f9h*a(Snd>>X++@5nddaoNLdg*IE4B`f2zb8HeL+Igzn!opZY`42 zUIIIA%QR{RjTf)8R09lL@sOK*5q$=ULi^-deq^cf1v?Ty;bVjOv}L!6rd@fIH^{SZ zC8*JH7TD-ZMiKuI0^^s(p3`Z2L=@)EY`lcHCulsZlJP^${gnLUt|6H!mJt`NdYNaQ zni%}%FP%BBpgSq0Co2EEz&HeT53bQVy=L-3ta7Y;NaJ)$+*JQ}YMab1Sa2@D-WHUA z7myE(;u0K~kvc|4UA`z=&#f;NTP_Grt$^hWr9#@!3g+8@y)j3l8tweMF&zD?X#(c* zt$Hd$Qbym;ZHi(6=bHpZXcUr)yiG?mzt|b|iEWC5qsBsBy3~ zd4PhD7C)xAbstw zPSIVf$RHBOD_{gy$(eVj__7>n9y)ew5_M=?s$EMd+0j3xhrstR_m8f!rM_dEKHS5@ z2cGun7Tqx*88prbE=h}lbhq!*@ina@{QBOGm0{x28Hfg_ln;YR8(7f<3HWiu?)3KQrRU+V^Q1rmQ&~S)Nq-3aq&!4Rc0iG* z=w|D5a&7reZC*kfy+#6}qAI7=_fiBa?6wicy0Yl(P7TqG8;!Q~Ky(MC^OY5mKLqljT-$a&5dQwjHe%5j zmnW%)ZmS&_05;m^TNWH?G(q?J*|jxvio$7};s+npqn4p*P~YGavy>&K@n=yPntyA5 zO<^)Na=&Kn1%^yv8*6@AIJJi*VRy%nnKhlg(lzHdEl?+A#FzFFg-QO90#^P#3#g2W94v;B*r2Pz^iY}$5qhY5 z6!HPpEiYs0byuMBBKqz8Ma0k<6hj8I3zjZIyv(p}dGx9P%wxDamK%yH$=81OHLaX| z;h-5NJ5=;;O7pfank&?d6*Y)ZFX}^#=ikxV0cW``$%=;tkr8gmzG!Cq@COC54INCC4DlPFQt9aZ2AgOH;s4V$V(kKR;J_Xst11tsX-m4<$un zH)0pV{pyD@Il>E>SS^qsbT{FX+^HnIu_Jq-0Q6nX;VzP8@JE5zj0cRlK36_>SM&(dW=lBP>Dh zOGUvv%c(&g#f;H#b5OyZm%Udw-=o6c;Ke2+dC@p#f_ZBQi{uhRw1MEC=aB~5G&Y8A zVswoJ-9VJP7|-d&T8eLOmno1t(0(5};)Jp|&KP5eqvln^8>*!y@3eaiJ zj(>ZwlUF?ho5g=gDKT~72&Z!kJ$7ngia$q=EK4ZgH&=HNwWydo>NzuAaGoSeHyqOP>`t-ctA7QdIEg2IGZ6h36J^?#S{ZPYN3 zvIO>ij)5+OnVQ#^B2e1@962oEVm6$vIZSJ@QSU5lFynXnpcY&Ksxs+Ev!xj7^+3$h z>8QE-?T*rE?@PRc%6Ybj0J@a>%Z2@?FhNZV0AIEOQMBu0CdNXGlR$ZzeAkMQ1CtNe zXJ(P6ky%ioSsJ&E1sCH~aA+~bLk`BJ*K{;Afn@?1g=P7jCdfHEkMAa8^{j7!7PmwB za2AgJ8W6yNSE%I{^^pb1xR=PlPOv#f+Z3>8boITpBT^v10pb1c?j=Qk7^_(KFp%%y z=cNnowNB7()jfQ#uAtwG^E&Mfyggp-qa)$$?}3?C{brYEtBwnPE(I&2>8EX>5q*HWU}S0yLPG?67F7We7ifapr7s3_Kg^!GCYja~%3pkeS!ZK3{D z+c5?lSSMfIDG=6GXom{t7-KSDY-|O9oIl%Og>Z)7fD~)P(!Vg@!Jze7I^7o|^rEp1 z+M5P`JP5LK&f8b*vr0#7L6_28e*yJpdo6G$$QJw&+A$lGV@rn}$DztcEZdk@b_M+d z=pguGpH7W_;|ftAP>Gf#yrD`{jpXGpYQ>g%lo~apRV+bg2d`A_Ic5u}h| zc$aw%!y>pxG8OHOX$f)S_5;*In5FIjS=MkDPOl}%!^fto5^w1Z$`w^S;_3=&JZ|oQ zd=~PU_3ou~g5+wX`BRU6&%DFGQsjS1lwbS1{IDun(Tr3^t*6Wg82IGfS!}D5XM+uP zi&EftscVuOlDr>$BYl_r5~Augx~X}T55#l;W}G%-Mw!Z zYE%E`w((J7;QD+BuxPDz)>)@6Bv1!1g7R~@;A*RfIz!_Y!BBiDl9s1fEhlZoc}k~A z_kc6|Wu>_M1Jx?{DOxJLDp&%$w(C^Bo}^V`-=@J47k`n~&EK+ydAWyZ=Oyh__!c0G zpV*-A<5-Cn^2*vivkz24w@_0kyBQSlu!rl&_0gr-K*h;1^k7yN5eu|IO$HpQGx9?S?U zM7f&>(TJkDnA3$~niNm)e_k%fba+}OQ7r8rOe zOlfYH?z0%mgInzBlLE8e*FEywai2)~R@jgnWG*tgnd&4vu&H7Jf@YwkKI0|m4u&jZ zBu5_n>iK>I!+GG*!n&HMq8T(Itk zJ$~^SCRksmzAFYq)E>jT-lwNzo%f4r_*9y~^B6eAjFWTZM@&l~|EX>!Ca>5nbkV5o zS%CYQJ2SC2xOnbY5z%tPsl?;3K*Tn?aQkKbYp zTO!J=@I2vPK#H=NrtRlRg5$++zb_e9Wm4h=gVyR!SwAU~-j!stYooSKgk|YNK?0Vq zR#>XAK9mIC3|<5NT}=we0|@FKG^2_V>$pwSj*P0H@KIyY)Jq2XRyO@YU(eiH@1ZiU zVX=#x6vmosJb=bGYQ$&D_2c`inynDvOpmgNs3Nf<<6P*Ws0qnb1yKB78AJ<2$7+VkK zG=jvPInnS5MnciU zBCq7SZrDN1!v)lj(HCG)tY=yG%5*}XOCWEmLA=gy;GU^rUQ@$AvrA(DX1RLZf{T#z zpgcVWJayIsoc>;Xs{wjqO2|ku_up<8eIacAtz&`WeKvve`r2e5hbXoP&)N0ck29%ZeWvD=(bToRTd}Hs=3-d5A^;OYn99B@|=mq&i>TOtLej<@BRUeUi{E_fBZ^m*V|qhDqndYL5vvO6BgRQ-H6|b$Ghb?O z@IsQpb~T&;mDpfI83STIZ4)l$q7qNTp!V2o5K{ZLq)4_JH`fdiStJ*^t~yA+*aBdc z^{1p}*LhJ^FIviq3BodHiaM=W5OQvZ0$nj~vl7g@jJ7S0+(I?4j&wXgUX~x)CyX5G z3Ce#5x=2B80^}5>k=o=`_VKepT%%t+5wm|K?Iay(4bB^TM)wPxjU&O0mkH{moRddy zgYEy!S-Ry^_P&h4T3WYCi4xb<+BqGg$yWS)*BxbB@YQczuY5qKUSmBNtoU2>m_8@Z zkU|#O{ZL&aBhoe9Vk=4X-lo52e%Wd#iU)Ez=k+`jto>@T0L$6hHgf7?9eo6OgKd>G$NM%` z&;T21W_2xxb)NtDo_ah>`P}_`*-bx3IoN)&Upk{)V)?*8M3@*IS94mPo(%vr>s{~a z^rdtPdX(rtLMY?Iii$r6REM?H7LM*th-|-*y4|aqh>;52_@b!Lbcjj_d)$7GnFJJU zGXUx5;EQ79J?jhY!y)0}BflRkNz+5F0u^hq$();?{c5e&DIiE>tBSJYD8<~gKn>dPRj$HAv#n5j`fe33>#R$jPovj zWweV{y-g1FrZWTBZey%iEFoP|;b*Uuq0~j*h^UV?YDvofK%oaJhCLBeFFB%bM>+Qi z=#SQT2Kig2K6TJND5O!O{t>t%uFJXCcguQUYaA_R4}(`FMT-{9d;4T^R{V9dpl5w> zzlDJ!`c77uGUeaJ&kHhKe7av~4)VpeF=BUO9r5j?$Zi)sVhd~4u}-XXffb))uCi=N z&Q^ud6tYz155kG$b+8Dxz1A=kKdvEACVP(ZbkF$whq_1f{OH30>&)Azd-p^iu0i&z zD~B!?UnLxOLjd|Wmv2SdtPUSt>EDA;Xo)Ya!m}pX#0b zQT4DN|NsC0&@6xd|KZ&D$U=RCX>T%-03hn@`;z_Z2dQu=>t2`Ks`!wMqRply=!C=4 zkB!eT4J*Fbl)fQ$SK;hmUPQ<4(7@9TAco38-#g@z$%;v}5TvE1q{Vl)YCk%eHg=Z4 z<6Ig0P5;jM*!zCF=@l!WBkxRcZw4oOFzMvwH&@SO{*5uIB3FWof1l>GCH&DaE89nj zZ$5zKj^K|g@ae=$R!70!01tQNuG6MnCcmH9DvrAINiTP`kRBd0Ze<&eCabjwci>FO zUgJmPq0^GW*lUw#!4*bgVj@Gway?{TG0nMd`S4NnwO2H)jDCufgbc{kcpIM^K)RMG|z{-nMFF`k6$l7M&mx6>+{k0K`zH@8c&Lx(1u{t`i2M$ zQwS2}U-^nW$`Lv5jr9B6Tyk!b&<`)`eyv2UzM-L;1&}5GVnZv+W8!{9%s0+Paf^aJ z!)v8>wR=K)pWwodQt5Y5mPPel1k3hQorBXEA z@mmJOk^fNk~gyopO)jZf-`f7jG3XB#6;O4^7WlW!B7_YZ`=> zyy(+fEFd_N&0CnYxA!OX@te`@3NWAXb&P4X8o%-t9YdPz>tz0jA)U4j?Z`-6(oR2~ z3?m2^kF+F~Vnif2w1_!lf+%;1U=;dNRd9D#tTH{yV>HqX6{9nY7U3d}=xP9q!l_zB zN!$YQ_6=?_U(9BiHCEf>KEgFF(BBogBJ1f`DG%7MV+MQ51-dv8}mm}9gaqHOlJz1761Q^=fX0RDD_C4r~{ zR;W>2R`hFHR1hkH>nOnIMC5pJDM(QVoZ&7pD|#z{mC#A`swUnF$}#!VYeIH0DS~Tw z#=(#Az1`0*gQpb;YJA(A|B8r(OlWva8tjtdm*4E+{}mR^t>SV}G?I7Oaj6fmqBA72 zX@a@-kG=Tn_{dx6$PV!Z@K1KExHUwMJw5dxIAv&jPo+@ozk`zE%)9_&IvzGKuYnI- zeb|V4lqM6G?xHoSTQePKcwv@OMQStGyqOx!nFu(7!Wg;rc+udlJAwpzp5Wyy!Q~z@ z%G6IaW`6-GEh8$>e)s*oCKD)}f7h`bm0>gYYk&1_geJw1Q`IZN3TcbEcIIHyvLGt? zyTTAD2i65F64Erb;YJ4RA`qPE@&U$6>pQY3Zk+YuDl5yr5Ss(f zi=~&z779*XrD+(A2FI%<)e0+k&P)C$>cSy=aEgiv`xn2`D?rZM>e<1V9wQy=ib^y$ztwn+ zz-E@PCC)R_0A7b!`k<5kT%s zu9-dx(5Zm2XtChgNJI7p{A_w{o(pB?5}BKgkG;#G$3+SVd*)xZ()q0*#QDK)IqROP z#4@K|uHuH#dz^r<@I{wykBfafk611gq{*Idqj0MR_!tGE5s5Sor~{koRxxn6zC3Z)iX3ekN>54WX}<` zcX48i!?O20&GkKXYM!`(1p(MeE0>qja7bdZx1y0Je~CC#6xNI1cCh7e3%tJC7InTL z6;})W>X$~FGs>^{OBC2sP=OA;yco+B*BrZCo7-7a5Fp3sG8B|#w(y70yOs3)3Q5xc z;-C*Y$NKgi406%3=9?U+Q-BkELL+$9rv=AIJMT+F(VKzqWGQTn6jjdKI9z#<^k}dh zJzAGj(qZDpfNpG$2=cgK#nHk+Q=7^u;vY%gkm(dldbi9IFfoqtYJ`*RhY!-@2Jx?Z zs*KP2*EtJBqDqEFjZrqktBL!ENa5J01s{0KWvqV%m37sw|KE-Hy~`0VuGv~+y}``x zrv$;C;&DrHmg}9QnJyr{U@?0v-+FI3?{W^`(_qn(x`&?@Z;R)Px-=E`0ICQrtcg$o zWqB)sR>fB+j)Wk9x;wtl%(!T+g0kZaw1JNj`I5X)3xLdsX?Bdg&#Z|Ft6xJ-Bj;h0t9Q zDmibE`6Uu(Rmopcrh#q~FuGnyu^Yo-oHn`PcZL6t0COi3iU9o8xrgq04Cprv!SIo% z>H1<5{e)0Z@0A-BsXp*R`#56l84nST@tx1F2)w&BAcK&%QqpRKxqxYvqz0vW2BEcB|L*Y^V_Ut6kbfmUc`$T2Zp8K z=QN-?qlGvCud`Dt92nzcBZBmNW^i>Aw=F)I&wsuQt>8Y=Qp;FOEt^Frn=FnD zLp{;Hq5QIzVCxZGNxiXV@!$dDEwrVVR;Lr|8*y(H(>h*fiAHlLs_q&>%L9xwP3np1lyLlwI#gbP?@2> z|M|cTnb7_8Vrv$DmHjghl$CEL7F&N-4yw1z@2{zrwhs~mUMe=cS- zNx&k{OuEef6#cLNQ#)em(l7m1H22h3e?MG>VJnEy2@Me6dQl-su8~EP!xBY5ql~Ba zH(`seY$=M_rum}43Kew}*I}Q?#T8M@^Ucp__a-!X@%lWupXf z$nBA(Dv2WKb*M@WzKg(r#iTRw|8mx8>kTWI#lNo}oB9S3``SKAhEhN}C-6L#Y2*%fCrtN!RKI`XyJx_{588WMu*)?>cq?MFG>=En#r4Ior+p(mSPpFx<;zl zi}3uteVMUq2PTKYVz)vk%ecVFvh3YE23zt_@UF}83E`$FE1>>nH;PZ8c|)U}F~7HT z#N$4OjU#3sFmHPA62sba9FR4Tzv$H;SK9uP1eT_SNj;(U9grFOrx-!?&~!WTY_%66 z?+xH~V0PH{NU;-0sKIZ`BSO?J0x7`{JNiY{IcgV*F9#9RSt_NEu*E9HL^KyT{|fF@ za^?P6jN2@a1h+Giu*0;z+}+J zm*ame%oGKoHnaEtcP6X}QLOQzkT8AAG(OcrJ%?=G-iFq7-n}?#4ee4m_Wlz;qAx5# z20c~XvA=aHwhD}T0uTNF|IeavsTgm~Sh3aUte(HUg1IkNVzy7KlfD-noPbmMK9zg^rV#8}^vLy2gude2~MF&!u1e!_1FO$R0%zxD&AO zkxWsd;L9}@qDAU%1%g0=qIVaTK7Y5cMC05LT?<9V`fx#)&3zp>|C6-YW%I6W0~NQ( zrsV-&ZwhdMire^af6X~)Vb>I>)V}m;ARHx0G$E0zK3-Vpo;bAGo_f7-QP9=jZ5|hWg0jsh{x5e?- zLI%Rk?~Z4I?5rwuXv99}F1}Ds3dGRR%Fp2;nFPNqwr`K7wNw~CA#{| zDmWxm5CtG)F&WfShJ9F?8RF&YnF?-N$}|$lTk> zeSk(%h@C?C5%4Va+hp zW8(~RcuQ+LP4+SW&c081aPb^w;QoBDpW`@SX91anec9>hcb_&ixa4VEH-@wXM4SC+ zu7HaB5hPYZPIXkj|9~3$Mm5QZmfh@TWU}cRvQu_1&wu~`1pkCi$mgH`=^XW?&#!;` z&R=o;NkLf{8&(RN9*m6DJnWp(ddD#Os16U;)gtp!S=}rNIy&d^u*uL{ zT~Cw1Dd2R+0lHBY@_vd8c#vyKoVD{=AN;nS{i7c2cHo-9(M^bqXn4n}c~fH|=Wq3* zS(NX3-UjP&**Wec=mg-86Xxj@T^bK-(iM@>hK~_(7}+V%+|H>zD0K zR!*^eEQ$w}^k6q2975U(HKmR$RyNs1~!cMR(Aha{*C;Ph7NAlEF&m9F1{X7wcH)8C15 zsI7OvLog_cTElPb0rZc%0{2Oiqraq!{nE?O^kd$LXaGu>DX8=f$A3fSs#K%;d?JUs zgoo7Qtgi|#=aIk%qaxUj8Y_AH!(Zc?K=poT9wr-L@Q^cKpmD^tv`q^s%T@5l?-_); zSRZ|wkQPki1*$LFl3}7$EFlAO5bf4Fj`0vO))v6tANX^E**bJoU8ou6)7Vr;pQnsu z;6Xh}D-}(I0l3B#Wqdi3UlUoDKw2A@f$XdCX+|Dmp5;3VTY*jgG9nABP@;T9{Y>B) z=x}Q1-G&tUZXw2*qMxv8t?XpW=R^mzso9Y>OF}23VC}ZcH(~BHZ(ti!2oVhCvi42( zgAbiLB-$GD01L@vT8chD;hSwDpqNc3?tLT_Rg)aG2J?~FA zC~4Wl4%7X?unqq3_phF=ri%e`Q&j0q@7QO(OG?tu;3Lt!uhDxZf4G;@pS3{npv<;9 zL1rF==oCR>2+kbK^!Vqk`jiJ_zwwqqN{J)BB*hhVvC1NDu}F1+I!-^$65@Y|PzWY- zl>ff&ObBw&< z4&Tn7<<(o_K9WrkFNL)M(4*i6r;<;>cagU32pfew;9SzRi?RM8?rhf?u7oyO`!^aC zY^1Zop&C}Y4MC)h!omScu~ny>5{TO7jsOC$;D4x~fj(nnGl4T89+C_)mkaPtXBh)D zv83+jvK7_U78iHNU>i-7r}=VbaVi{1`S3C;>|K$h-{%heTfjp5V#g>ROB>FzPmBwqUSy^&0l}MGSMa2 zH@_J&n8oUxfV)Zq=)-v{iqFk2`_NkjW^(&GIBLrXSib9SXhM!=+J3-&accDT&VP_q zG;zj;1D!|xuZj?_H+N;PM8 zX^Q%Y#8*fP@Mpe*>`edzTIWB9Q~n`_vbsKlZ(U3q`}UW-*c3G|!Mw5!wG*Z7>Y6>o z62HiYD)fzO&9hXoN*`-iT)4;s(^EN=^O5wqlQ<8puNrex2 znIK)IBm6|jwODN|?;4YfJLspJp>y_uV%_4NFH$&e#*~1!b1^s`Q2#=JawUSso*aSV zRL}F}-Y1C}dLg0OYL$J~67PJ%_&fkDQuz%6M#2erZ$)=QwRESe+ysb1$bUeFKT013 z{V3gB1z}GEoHCFVZK-kb_}F`}7fcoaW1QB{7vOx(4Y(YyO*z*{BU&IrCVx6+fE!hD zNJOnySVve|`V*n8)7$gw!c5|qS3JJGFWHM3j4se(2M7X#`RO~2LasbKeuQ-pT~Cbt zw;&7SWQZf~L0M=hXj5Ycsz={hR6Dzw8&RAiE+KARtWs5pR#1Tkdr_CT91b@4Bp|?{ z&K>qLKr4ISq7;-J4wThz(&p8!V;qb=VWt8%`R7*5pgbOfPIt}m$Ca`4ZNJKEOzc>; z%4J!$^L8#;Lu2d!D+~dFWV@hS6 zta=j|D1iR5s&Cyph_-~ZdYjowB|ldxk9Sg&%DF3F4tCbfikzZfTbc+)u^mT!niXu^&Qa=;e zfd{F|!Q$bnKF?&l=FAV$=hVwBe@m{I+pJ>H+5lD?Fii%V7a59MGPwQGf!8y=Dj8?Xq4s?cArNPJ+`4E0m@o9-mCYdnh9GM zJM%j|78FthD3F+!Zoq zzGx&;sD!@x_oJ!slk_s^JQ<@!g!Ed(=P_$wLw%TimcTKnPx_NLho?Mi)6}r#bveUM z67v{0P`&y99mSu^p}T&MXx%{O=6D=ya7^yHr8ZxzwGA)?Ia&^dm6j~)$6bQ2Pi z00x!sohkb`qM7b2`yaDMUQ|4yuYi7cX;g@E!#@}f!swxdqLXDT=W%hftWw5vSf9($BB3U(`Xe+gL;`p(y%jvh;f|OJ7Lhw47pUfjyJ=|r6CXmyjCor} znQRM}#3AV%XSAg;9ejq?;r4|aZD0)X(FE;ZLv%g$7%q;q&kBOw_0jaSK}y!7`EFZj zcc-5Ukzp7|7n8IA57rbJ8v5u@iuSh+B_k!V$o|vFX!0BBr-}<^EZPt;@wvRa+So?N zdkJBE@(gqY8XnKl)gt~2k$W{FQ8V^Hjucoq*2R^gLN$7~erbUC%wsITKl~Q+X*8fjj&4wR@X|MrRe46>7_X&xHN8o)}66&X*VEAGm9zwq;#NbTl zE92Zu0-_!U(rKtChT` zX+mZ}E0FNXcGF*?vMERR;5wtLV^kE)i3*qeoXJ(gL=V8^&XF9M%ATAhaW|8^C;&5b zb)MT55T?||e9*)Y(r_a$+$atnOG*Tgm8fN&EDn1e$cA1Q>9^S`-}+3(UvToc@8-Xn z$<6&-OP_XZ{<4XSSDSiRyBwfJgJx!?Yn@&$z#FfnJ%T6F^E%`|AsOZJ;?+jL#+EBl zhU3KSs%OFkAe-FvFlv(P94myCMCAGaB@Ed>^+GW)7aS}0!RD}m2R?uDxR?wGF*+~t71qzK9 zBblKJjr?|Dx^_JpygOQm4(yqtfLz}B2?m5C|J+jj1i#@9ue0O=;|0LvP zY_|}?gA{;W_~$bH@eaLWZ>S8Ia=hj#u1moqjs;m8iiFE2JBlAxp+g1QKpp11c=-zTH=*fW~WJ(3D6Sm4=Qcs z{XXq2aZ=nKE#d$MR~O)~#7s3hd^<#Dj_>Y_h$akc{dn|-@+mQE{r{xr-?NySgo(nQ)S`Wz{VONwl}p61 z>7k*i@d zQ@{0mM87vPh#fIJzYThM0?sj>g-1U6^!05tc?o1UgJYIMa6?csb#duw+71TP%7}bU zfEh0o##@gU|Nh`FmH`TKz?Bu4i8Opgw>XXuE)p93!(MyTJxBETL^W>CoG7QuVJ;F~ z9Q9{oit@BqPC<@3%fQ|9XX*+sSTwa z@8_Ux#vHN_)S`Y?Zh(Qhv;0VQ+Q|a(8x9rEp2YB8@kiglL70H(9EjQK&?+t^Dy%i- znA#K3RuzZ~YU|)RWX3LYIJDttS6}<6WsUCm`NHgZ@b_yOM&tCbX!+NtcmYBtmQrQu z*jY}S6>FnW(TDP#s$J~rcbr2G1Q;WAShJG2uzg%sk`DHR%ck4u2dk=mvmpHEFXah1 z10AQ>O>wOld`7Il3zWX#Uc^lU1S$b_g_;DBqlo7ljW|H>B^=Z6rM*S2!dCs#P!#cC z;I>CUYH~Beg_}5c_7G4n5u3?`t9+?x_&Xh# z8*p2kBsvb}N3;rm{rWzu|3o+Z#X=7+Lhfil6f7>X$WBkPaU218mOz5&5)T|i2OM`XqYR_9Z83zdVC^J@ZjE&sv zv`IyKRh{#}xIn2h0uTiTV;{+Zk=nc2|L_FypESHROr9H#Ca%X{tx_Uxr+?XDEB4-hyU*+cmJrlk zH?h*Ynwc4I%7Ux<@Gu&CGS;ZBSN&ADJURc;n>!+=<(OTP^&cGG52fca0oP@`1*X$8}=V+W#ots59xlI z1)5MX`t!$8+S$W?Xh#yh4M{iA`54tq>r(xgclyKWJ|g)cq53C!0%QAm-)(`h{Vp%t zvvN_*s70>UJIx{`d)?9-?|;OB#S;apPlV&^2{CEO(!a#1Zk`(R%J5arD-oIm5y-Gh22ePYiMBR=s~X!mLQ`k(=% z2`H}xx;$<*f^1s&OJ)TKLx?bfD*obqtO>7i#npD<*yKH|$d9$dpZ^8@(p}g1&SeKUyrhpBBNba@K$zi^caE^volODUz0E7bLk`a<;%}BM)xHURT z1V*=OUF0*v%gV;0G{<|JDth%))$mdEh{UIWf=}X0V;(EeMC2<&iD8elH*l`ouM|lM z-k?{5w)zIilK~?w*zm%GixjK5GBk0pBv0+|aZTfid?(T$Dl%AR-xJF8Fq&;L%LZ$W$aI;3*U|HGjdbz|)au?z5W=kVWeZ!g7z)3V^!4&}15RJN~&FeKUVMsvd0 zV!aU&td$LutB=>aynzd9gqSd$gQ5Ssz>aP{?}_2hy0CJNY~rXXBxQ^Xv26)i`oR|{ zqKsuwi@N(h3qPZdwCCY|f=R zl_KRu7zoPcR1eH0&g+f~QL93jQ^J2lS%|hI*jdGyI?0GPij^nzzdyZhHEZ}1m+9bl za;9&!u9spZQBakTZ$+TmA5D$B^@w7RJD|m-TZ5SIu>jTub54#Rq1`&oO*-ASPYx<4 zk+U#1o7?12HWe43vs>g3bFEN5J#UJ;$Hc0H-J&3&ntAp)W!kLRY2awwW8i9B@Q@Xq zl}lM~o;SAVS)X$Q4+AhhRrQ=I2gNsPUP^%LvE+4uDNR(ru=rO1HrM2lNyE8}P$cCq zTq5S$b%1k38WH=N{{@>a;imJ@B!g=eR&pP2GyJHq8F|49sWeVQ{Q_1*B8zPbbQz^D zRW;SPhr+2qEULV;*&7wyF0k+)u9rt2Kvqu2p~+}|s+?{Ir^Rw-2q%M$i(2GrnkU82 z{^tWc-M~~2C+f6}_r{(0NII&h^%AD&{sj!k+OzTFjDx(bcxgb^onhmjfz4NsT2)Jg z{oN8JO3$Gp8eS>#r-b<{Fq+piU|bPdeOX2W+Ix)*Kl z`=fYTCOu{CQ723xolKYJ7mb`|ZuYQLQ+kX^q%@t6m{;*ekFEct#h@c~i~~W1Dku&$ zj=BQ-neX0MN1*@gofYfe6sPvPl+I;9bYrtd-JY7`*DID3o%OEOGlcH?)$BX!#j52+kWjFej&S+`$?l zNFfiXR)iiw;yyFnTvx83`gvb2h8hiW2Nq62L`ex~L;X}o(I+Nw6=18Cz~40QCR;l7 z{i$%XnUcww+gP2SX^DMXNgd$Ncl?3qk(ItpA;)rpSAkN2d_LefOL5%>Kvq_r!P}yE z&6)E1JZ{$Hd+fnY=t$F_@x~V=`oR27tcJpI2wt^d2`|z>sBr#}1L9E?#Gg^4n=u(^ zq_0Ekhv1gJ(8;G})IP08X?U=z$-LRtPB*4UD|C6$>*&spYl_P=~$2QEJ0t##XOHe>)Bl0>se}C(V?`yqPe}E zqWI))Jj4mOS2KxTHL`@mRbl4I`Al9ax@K>R>%x!A#B?L(5=TET_wBtOuD$-;Sy-qT zS9ldN$7h0fVz5$U>+A~BtJ$QbA_p!$MPV2c(*^4cpH~BNyZ2Si{l%2yir(X3)txVf zN1s2=kzo&ux@xI9+6Ka$eF?HAb03o@T4=$cyX9ix!BuQ+Jaf#$@bCfm11*$lW;T+i zk%{}-jLZ+Tb_WRgElTkE3->e9S?Cz@p<^TeycjxmCjC#-Btta9Y3Ob=u$()k91b^z zBSmE$Jr^D-Pp&v%pUDn}4kcEO-&~jI$L4==(x3$jehD$}PgKScf77b-%vfzmO?Fu) zexHnYqu`U*R%WmhlM?jSI+Wi61-O!VXVLx;lT**4j3c_25nF4m+GLQW#g zYt8BZiH=5rwsZVn++nyCxkYW9QE?(RX_}Gmes48#AMrV3e__8ifhuntR{Alr} z+LfoI^}n$vIh^0_nZI|J%u{>@LZ}M9`Krt6L2l=TNIr7HciW|AdYY}B#J)o2?=g-f z(+|CR7s#RfS>2u0m8%NFd(ykp$P>8jvFyd?ld{47V6l$gMgsgTq6yd+y_>R%N9}n43aoG5J%dLn#w6D9l}30 z>Gc6|EB%R`M+}jiRXrs3B&D4zduW|aNRq=7#;`v86PxyKjfgs3d-HOodQ9(2HSeLyCR%zK6A#Y14MH9E6Ft+d z^1hvi_`mXbNyv@z^G0wF^%_4%HF-vwkla>G39==~zP$MG2B9+RfqifKl9d+$_-&U` z14@Q{K>XDm9%fEKdn^O!LAfbM%PLl!>9=33jAsf;%WIkeE zsmwP~V7CYyZJBErHL7gxnDAzBJ`}42mwx(H7ozEXuXZX1FE6Bm7GWga(taQJ74)Hs zD8}%T{&n1*R@%q!sWoRLewZgz)61r+^@BV1ZNgw~&n_90dG$TD17J`o`o&iN71OEG zoAwnVX>Rz4nLM`D@8Euxmr!-KecnF_%>f;-*`QR0pcxdhV_2Wcm$kp|LEx!1o1V$0 zw*U9_TSI-Y>uwD1_|VM>y(T269YT?udF-)W2xDITS@0^Dy5e*dOAj}K`9d;43@RgA z%z846mLKsPUi8Fsemkx){~2Z+*j3S2lT1yDvB#c)67Y}C*V)+Qn?0cL6>f~711QlQ zpgO+?*5njI`B;|h<7F{%x~a5vE_2;2EC$^~8i^GW+~o}pOpw!b*#UuN_q|urElQ`# zmO|&s*c-I2q<$3f-^>)aKi&jG0L?b^mfz={y1*Tk>Cmoha2JdzyFZYJ7GgcxpD}8AH|Y~)B$k_3jBy>c8?IXSpmbw=Qx;;tUsE;S1LR}JJ^4Ru*qTU zT3kn7;n>cFG_(@0T$RC|9zcV=zv;RwAEiRk3)VCLp;)AyR?K-Z9mug^e`JhMV7zAl zegkrvp>`C+m$upyY9vbJq9h(!E@LP#sJtQJd`uHE3d;3RT?MPQEOXO?azt@bhTrXJ z<~{W=Pf9A4y%qHE{BF$So?AqiarLIG9sV)+2@nmQ;*rJZqT{U+ zyAGpb{WqX2f`eIU!029_2yWx{#&4iwd1V0O1To#3{YN=A*{RX^L!tI097B5{P7F9~ z$Tnl9--xzud@lybOH(Mr2{D z_3|urc%wvFump@^F>>dvf`*!0uFYVy>UhXH`L&rzo7kKEt`K#dbW=Ule;M+daEzFr zK)n=KIj9T3L*Kyv5AXma7xDXazhhF?;)-Jw8JI;JxuYxnNX-$Px)?iTNfy+bW_Kp4 zId!4FGOxgp%TTJ;e#;W)K99<(7d&K$vPumO2*cEjx^zm0+bTEgOM0=s(dejDUL(ODOdjE*VI znJ5G%TXqYtn#=}`MgHiZFiT{jVdr?e;<#>YRk(K1wmJ2jfM7yS+E&vJ2~YN zF_RBY;DfnX=L*1;e5cgW9Q!&P1uhWsO6e2S<4?Pzkex&|37%+cHX7`ynEz0ihKeEm z$+f%Y(wWVtjl7RFqUc0t`9vlW8AiDL{Y8qa`TLgY-hnYck$|aa5-SWCAPA$VUca|L z9MTPdh%WAI3HPIJHjvxN2s)uGGF8RFk!I$m(1@mp{gZO6ABDvRpby%0ZWQpAl?04& zzcM%s+rWvbfs>&lcOI++&1=`D9ZGMBoE&4*zrw^FhHF8|#1=xFip{OGZPWOS(mvdk z_U*?|^S|deoqv0M%I&f8$h|`*B#D76sf&EYn;4*~4F7Ptc8ola8ZK)AN(Lo(JBu(O zC+&v8lE5-1(1x|ZZ0(tSood_fd~hk6!NpRn!eS4CtB;~4591R#pK^~|kv?V|7-tQg zCh<%VP($*XWt6OWazy^{0B&^E{)DBSd${1eB83J6N1CEeOxg*y9%VxrwPSDc(L{*p z7#B^vC8n)Y8dKVP4@h zNgz_O>^*7cfdwnLcn!B<$1ga#b>_GpdzG1h*cF3@s=)uJfu*QUR()G_&}?2!q-qtN zSfJ>D|Ls!rrOgYNc&d+HQNtKF0+$YOC0NBdlWElwl6o(uGowY}P>$c{Ec|UsCTfO* z*N12G)%%=#7Cdh6alFPmD^8QBR^UBD>;9rUwlRtoYKmO_tV;~vR5zsR~WZ~x+=cebn}J1*b_o^sMBnpca?7fY33eY zI9!upI_MHf;W7asepkh>yxS1cpD6V3EUS`8xg@u{=jwTL=6d;+16U#>HS7j<=}jF% zX8&wn3;3lBITN=Cui1PKt-7ET2Jx-_f|!RiHDAaxHC!=(g&kk34$&ul3Jgt?(ez(J zQwpTtOn?l>4j9CS9DZQJEXHf7FZ9{m@Z3|&4qgbv^%dr?t+`1TFD7y8Adzu4>MRrGznHv0vu zF2ZOsq5V~Ftl#<%-c+il?$E7cBA2#YwZ+P8d3(4)S zelRM=V*S%7K9K_!DOP#jOks1TsfiqYmVJ5aztaLbpCAy7jIXY_l!(trNxJ68!h(!8 z%!!z9yuJ}WXZP%q-W|IzH@k8a`NPmZHl|ZiX`=@^J=L8n0>fq(KY+z0|HbiS2 zx)8EFalt6+iQ{9`mc*>L zshTiaSv6OGPFWb6zH{A8OF(BW79CqOn$p4Lf`8s zX+t3vxFxg6qC3L*PDnH6!Eh#Pv%jloz|#0&Q?TbJ7=n1k7q`A6lb&uMPa`Np>O?>H zoCtv;=m4)y+|oi#D;qb~OxghW#y{Y1EWRCrSvs*ME9dV}&^0*b&9O$0i(Zl}hMAm? znCMyuar;w^$OivsR13@P!-@_BZ`VI5jQJboKnrN55rPUYuRA@Gg#Nr-k3VKy+4mbH zSbNR@^OY$En>)pYXHy_CqlHQxMjxs4TI*%+u(ydjmXJG7b!*WT9A$1?z%lD@FLvTv zzGu(ifQJ;wMfE|?YfUza!@9k&>w(R=>(kfkHTP@=AIsT2F~a)!8p3Tg3<;xpsThCA zO-*Jkl~pIJ9RVM96LeJCZ(9I&>GG)ws@p8Ag00(Ua4TX!Q9=1=n=ar&1=i0=HH5^9 zVaId7FbZcH%;7oP^@!P=0002w0jPn9U;p#y){ZrcRU1%h^*cE+soRo;FPdE*yEX(0I0Tc}dyGaC^NfM?Q zmjQd{2*S6C#|Rt%?er$l$A7|Qxt>$_q3*a1R6nwf7^b493xgrbOr92MP)ZT?<+*FC zM#0ZP4~Vhq@mmOn;oMKBwhjpZOV>8v0AwTr3(7v~xm9oAsF^KSpY`(gemh^>fIUVH zht52zm4^0 z4Faq5f?j)!yMRD+9~n6-0C5EL$WJ#1TC8CA;$Z-{jN^lJR}Q?53O)b|ky2xPa0P|Jr9l9LIe?Zoq$x!K#DC)9RwAtws6^Yz-`~RUW(=KR(rJ$$H8PkE zzt1(SbJs8yNHJFwo8<0-t5BJ2~tWAV4dG;`{518damG=zZA)%NdnML<%Y7aO)URqAd zi)3t!U$%k%Q+k-`nK!5jB#f?8-MGxvXk7~eNU{r+R|>j;;@K%_CCzT94zn?;AAO*l z>4+DKrqB%z)Z%5NUBh-DfRc@Eb!^+VZQD*dwr$(CZQHhOo70;&XWn{#qHa}P1)bLh zZ*2Rn_1)~}lg3P@8B`!41f^eT&|0Sl7?+$6t<1CeBT?t{hx9yaySL^@7|59VXFe0^4c=7=Tt_#($-Dz6E`cNN4dO#CO54o2uhG zKxRm!|R=GreLSn@!NP)@dC9Z0o1uQ zVh)Sk=TSTwDR-xCPt3B)S6-}661y!eO)Jq}u~FePg4`wA3I>I7Yfh0WnR$79T;d}@ zz=y#&%B?Th7V6DnBJz4Z)Gv8gu&dG?&xwZ-I_^u->zPnJqt_Bdqst}!kkljrl>q<; z)6pJw0T9(B7Njq0HtGXNb}UUarvX5b^F9e@J#i;YqCSI^DEYN=S^fzoSDQkhtn>p& zJQbqXJ+H!l@R4O;XoK1L$cUy14E5FA4X-0fwcdliiQA+%!z{FM4^8VF^1-~q)B%7+ zhy%X_XEI7~v+*gAY6PL~Jmp@RJZ2<|Rlp@y8owpdXN7&PW} z7cWn~NQ98$Md87p(v*svxvq^@k>Ne}`RdzC5FD@~{HwewOnA74;rLwrZxYyV zGeneNbpu^|;<7!_kpPw-v4q%Ug6d9C`z5wA$seubh)?o-PkK}bZ~{~co4QSNkcqu! zOAF;>!0Ex~KClZ;j=kAYzFMy!e!}^_{mePx`{z1X!te{R$%i z>sfU941%pzez>3@RWYLE%Qjs*^F9pMEE){#NT`i^LBy5XwVDAmP4svIo9j4FLi$uQ z;Qi5yQNx-wvM32oU}1q?YHk*ps+@)AyTU@Jf+zBg3V;O*qg&gn`2Jm4MUb28M$`4a zhB4NvM{=5%NnCA&UPZa3;L+bE=0D2JhAyLes~}<51f@6~?C>Ls3fD=`$*$Dj=Gw=( zht>G5TiAcvCQE@zxrKce%}<1599A-L?JV`u267^)cq8)Fozo9tz-mLjQsqJVi!?7J zEv*_uvS58Y^qWITz|=2H{Zy7GwU(dcH#}cHnSfoMM6{yyLMK8P7jz0uX@c4E4;427 z%Le~blI@Byj5k0BDf18x19vA}h_ZBS5Z?yv32F=cmQx|134E7+;dUfJJyz0;18x z7uytdUPMP5y4^oEbW3vg0gtj-h9et?baF6uSeFTqp?t;_lfD$(`Yb?!4S{5*+aRj? zu?f8}rGk1E1;WV|n5FTCJmNU}f#-Zv==S@kr#FC_q4DN&m*-jLVx1a+M}pvn9t|Dn?lvxkD;pn;MX9B5>nb3iDtu_}%xcjD?; zq}(oVOs@=jG`!`Rh4U4HE)h-wF(EUTBUD!g2f;?0$1};(Ajgl}071urL zF0*dJ^pAN_mjW^r1?<)9M29z91}|bt7PQj40u!ZFQ^_jB z(T(!6zv0ZL>b|oo=q5MY0r}Gvf5BWt`iak3>gG?sPTtc5)=(V2t0J$0#Z|0Qq0Wjl zRJ~GIQ*w0mv%!~r@05($14P}%hrhTUP#1qpcc;oOlx>9fT+4m14f6oZB^>2@)Mk23 zQF5jv>}a7hi;f*mD9)!}>18=4bt8#z#()tX)QH3j8qWFNtD4mNeZX%Ap2fKbfDcKJ z7iouIk2j%94ePLYA5P~wvi0fzFJL& z9ry#EfO<#>%KjJvFS;9`CJlyZBepkr06`BvqEw7f;$m16ebv%tHQmgpiYy;-{c@nb zLyVGiW)6MTnbfp)h8Fphme}{g<<6-$wT*fdAd)2#f_XWreF43jE1za-gD@LGR1>GO z{|1<+CGLPwrDnDp9O8I;gs1^kQk+w5%4|u}7u{s~=O8wj?#1w~)}x9;lC%ZX#yq7; zg|J2a;E@RGS=6}wgzN8rXT0SPw!7uah!FbPD$AR}93{wU%iT<ATSy4E6O256Mr z%t-1F0YJTmgC)BK{QP$99oX@KKN&?ObK7Sws5d|fc+{#xkq9-ONhgP2 zH1S`lX3cb#Y1G>73pMN0Iv|4fUtSN>pUSmwo`57&aj;h|+K(9!KuQyRL1%;mX` zVkT4Tw6i8(@?*It;Flxu@dLeQLQO z;JQ^gGaeoH;aWzSaidJ~_94DjsQ5c#KU|3nX7=7^dr{0R-E71<%V{GFfar%Z2YkJHme1Z$q2y48OGWFrdmQGUh0z9*cboTVkph z0k@!2#8#B}{meP;NE+q2jAz6vfmIhcxGWD;qG`{~yL>*?56eL|TT#@h<14!@5XNOj zfIb@;++VXrtB=5?y34Y#Xc^zlVY9hPK;~b2&~LF0SRTio7Dpt#F-L`o-)0RRRIjqi zY?J+E$CFQTP0^ssw2>mNi!#KvRv7}^W{O&|TYb0A z9W}|;5&3|p0#ZIIjU&a(_mcX$a7^_C@Pveov$`DQF=})oi7Oy!OG7JiiFW9dYdxc1 zFUM8IC7H|yDisw98hzTI+ZN^H<*+ejk*h-oc2Jh`0zLmFhV-_kZoE5B4?GP~nYk`^ zZ%oYeIrmCJrq5g-2KYm)U0e!Jx_0$ubx7MpMzSaYIPK|WWtqF1p?=)(d(0!(4a8VkzOV2N~dwrk!B{TA(g#HtaJBiv$8gGSg)Bmxd%PVu5fN0{?g7Gb)5ICJU(QkM0RC zoH)fUJ_^Sq@1Db$lG}ax8(3lZ3SR|}fhG~wyEk`N+UU<+$4aQs;2PBiQr80vyYW|8 z8V<-z67x^SSMt}-CRZ1hTFFG)J$c`R!G=(x?!^_(M3A~PHSj>r9@mLIZs>DLrki|2 znWPZ8^Q4f=`oNAnZ~xs=E{a{dvd8Iliqfk~a?KF<*VXLWd)*E1iSX5-sTiEgOg$l6 z)dmWhU;=NvR$0MU$rzw+aUx?FA&*0#d*w9baLPVcwX5L$#M6$&J*gzzKF^4gv*wYY z&8iqC8K7`451AD$8}FpLMiCp$vhyL-*HjuJ&}+?xoO%E$3{sW0itaX}YlQr$jPUP_ zT@;s1`rG1!BgNXuNa;I0ct_xfrkVMoEc(Z;CggU&V5iIvUA*?_Ojh&0_|x zU)@!S<4s1M4`lMUC^$A958+yGN&#QNY+@)$_yM3;BCwOoFF;zKey0ki65`bN!3mgT zfW(mqcJsFiQm@E z;|hASePW6Me*U6tf5v0HLHd5^bR~Dj==Evj1nHgBe~{h7Jhdgd`gHIqT`Pi3k?IIv z7G`$K2M$ED6T26lHZUOR8Fdo?6EUSwQChLGi zGX}kJ<#6jT@7pP-g%8ELP0PpaaEH_}y51(TZ?B2dUB7Lu#>}iCC*;^NIAx2+Jn!{x z*G}XQMiPtVeESlK6gW05$G8>3o~8L-(&d-=FfxhLC{}*7;@q|utMF6nzywf$fF)nM z5ENo^x`(n=pqwv>6VL(fWh)I-5=VQ}lK)qG^R;sk&1Ir`wreMlu2{1?N#KNw9m24l zl;nvnZ?dbbJm_LFjZ5=(9G&T3oX#yDZ!96SXMGwt7&o9G7+Wfc#AwuqSKVEd)Mw!! zt=ksBdn7c-h7CB5w)GSaG_!d0#rpbxnHYLKfs5-*qeWq8Na&o4hO`aX+aY3>04QA8=OU?o> zTLd?68YG4jy~~yW0rsa5yXl z;MMA6M6QHub|^O@bq^t8^^Xf^JYul_c3}MAZWlE{C_}X)GkKXU+n|xkY2jSte{@_i zvEb!n0qPhsfRPNyUL`;s!dE9msmoyfq@%i>IqB+s*3IMn{Duvll@|yWri=5+MwNYL zdt1xrIK~H=NBoX>8~{Nd9n{LXiuc%Mpn~Z(NSAb0*}u9&%U8Ii!WDJhaC!};;*}(R z13>?_D4PU#2lx*}4v=Omi_^luQIf`t7F?#L)vAK;o?2PlO>f{nCB{Mcv;XyoDUM9s zL#XL-A>%1lpyr076SXOj2!9)9$)drh8FNyd)=d0!g4jE38E_-qBQW3^XH5V6GX15D zoqf0%XPz-mU1ic&wQ%&#pn<2g3q#=RWA*wyVMqaM+6-JId)PaBidW}FTU@r~AZf%| z3xAZIYyVF;{}N1rt)Wj*5r@rng-M;iC&6RG9DI(4)A05BI1=vaTn0b{PuBc;BZenE z6i{9#_>nJlhtxsE>E8{vn1Z%MDBgDs^A3rKbPCVjra^^)58)2M@VGi1vf)oeErk~~ z90RB}b*4o0Eh`#4*K6uf|GgOaH0zEuYv=(GEos`bqT9^zFoOdj-BsiR>@8#;S|?6 z)}~2%#;xzV+^Eth!is9CjaY61ya{^Z!r%pNdebHvM^ezmV1Y${cO4&v>c{Gq>NGl! zT1VGk0@|+WU9UG-K_NFP`wIW9CfdygN4_K$+sKWT!c$lUG6zD(yKK<4zR|l?d62L1 z)7-q}FG&Wi_6%c17~TYhLzGijzCMy8q}(!}iV`5xFbL(G@$MvXWHFI%LV{rryb+e7 z$g*==Cg=vW{4IBNO&#~i?@!dr;i(L7A1bcieEHq*J~IqXEgi1P+5&#j7J9%hrUC=Y zztk`Ro;1o7D+(pPD2xgy3p!tkEbae<3NZvn_(H#tzxe%JI`M#>nQuRB1ZN5cNFcH5 zH$96jTS9`mz~2QqF2okjGeTt{ohtkMQyNY-V2HWO_AzY~OgfVqfcw_Muk?jyxt)ZGza9QXkg*lM7Tl7R6? z4!892_$25=(D-NNP{epy6Vt8$MY4&<`%Z}Y6kzYtSkjJu`e%x?Wj$yre{;j{%r9tI z+|kC23viE?HOtho?C(dna%v4cGyMzMh=3cEouSHd1io}GB8z7)n&H&Uadl~f-Foi| z>(kyn>nT|AL|ys0;q#cTBHPHi<$gFWO%Yeo?~wMVg#ZIjKCIfs_!D*H7%lD&tn5W$ z$=uPSJqTqO;nR{~Ir1ZR%;NwlrCDuk1FSjtztYP&)zObY8R1bAY^fbKM~GmYzY3hm z^w?2%H>9_yd=EcAB(wSGT-75Du}I}2?Y2?OHMDtT+0VT~b&_cj)k$}CD2&dm-PzC;!$?L<>bfyPWhuP{>-qXGG6!M%ACV9HfLXkRR14JAj7e#qL-@I!-X zuDk#o>`C#8-FGzVAk9Ok6kG#1Wn_Fr#U+9)m`6BcX%OXE>d#i;PvBR^&_7KJljQv& zm0uJ>C#7BVpXM1?ofx4^4&YOp5An25E@dLK{S5y~i7c0hN7*qfF0P$DACv||1s&8V*e`Zt&v z{o2*;D=oBzjfEqg92(wEC zZkJS=Dwis}0n^wyDAx%PD<*q8n05g0tAq8T6wc2c;tpGtM@BlES!Tt*4JT)$?&kYT zbOKdQHxdrQ}H;r-uY93JxUA^s#XaShA2(HPIgEImSh6^0((%EPzr0UL@UN7MI z!T_zjs}CGi*Q}jxdyOUShKX55?_C>d6ECxbGKn=NSFm~G0HPQ1Gwrv`6A{WvfI}|k zLMwLseS=eq*YItFs)17)cU4%jib@1a!?8zdkt_rxvc z`f=k#oVja|U_X1o#<{!uWi0U7Dy88~UtfgvB~=nIT*m^PHTnuDufRp1K0)GtFEl++ z_`C)_&{*=B{}}_rRws#2TO(%u8_hbB0t>MZ=8=H$UL80B;ZU&IqC99N#eYuCQ|-E7 zV*!T>4b9~5Wn1KZgJzOCK3*V`7PZ-VOA(Xb!cwhYe$KsiGTx&r5N7VqE<(a38ZBE$ zQ2%MrWbUFk&yL`2=Iv;iE}-#L&~h50yc&r;ohatPUiT+GQ(QWR>1rUf|3P_fMpo`D0d&Xp*595l2LiIw z;L$kxP1P1E8T!@F|7+(N{KYq z{dy%RVbZ|ZvCr#yu{@NZOrpFVaAzb5?AqImaEr;er$4ZGBElgFw*7FZSM>YH1$>eQlEM z3*f^E-*q~B|*vn zg(@)GA*Sxf6`nAD>YO(kUiR_E`_x8Svq_by$C*bHc>%S{nTuZ`sU+bwn3WBBk6q39 zK?ICtdGD&Z!3x+~gt_R>!7xD(XJ>F6Xf?~ciLcABYZ09mF-fopK7At$%Ne(5SoSro zx8c*9c(&L|dw{4-e1Gx>-5MeR%^7i2F}!oZ)Kd%ps@mC|Lk_F+5KqX5qh_kWr|oGp zxh0uf9@{QCMu3NUh#~yWQjie^pqUPi4?E{*$F=KjLsV302(2-?i`)y%sS)jkZICUhq5BL{)?ot?b$Gnu-h&KXL=(1g>6ei+ zNEt+#LhEbM%5t`as=4dnHMZR7a_7`tPC-mUA8TY8-1qLPqZIF)2M*9w)0p3Bp=k(h zBs3p$(u?61z>l^%=7owLh~4ITnBZvmi@qh)6~bL#m>)!c%LnFami+IFKyxJehv^m| zz@E-t`0|8LH_o`Hudzcesr#@;t4`8=y2ml_E~JBH?0=-r4Pd$0zkTgz3dG?3XCwnI zfD|Pf1MAGxgngPy2zs2)>_XFaq9FH?dIoaxEMG)tn^m$78nB2UKoksmuY}bQFYYEc z+-*tfj2L7wymqL}?3Fpb0DeAz+7Yubz6LX{@c5uUBHAagKT1Tw(V`9Im|ly3%5T01 ziO?PjS^^36sLOkxP(^p*&i-KNbzo5H+lp8-Zw%4=!ZeC1mS}?m&1P>;pc)2%(;#;@ zX#u2ZIEQom!^3XA4}v5yo|V7fb8@ind&pucP450PmIeC`RO${Twon*gVwdns402NRz6)P8DtK*JOVwz#K;jPZS+7nW0W7^`Uk6WV;LL=;VGJV))JH3>)ft zC%JbM1jg=Z)1f!(P-8k~8N|+?c{||V=h8#cRR5WfOn%4!?gp0!okgq9MwaL(wEb7t z5a;BS?(zTV7Cn1oY)pr$)Lp(7h01JBqGvF~(O-}JH4i*m2q;3-noL)-p!P{X9D}UZ zg_*a~+0DrmZcs1dq%5!2Fjt-kwU9w_t2~zb_6W(QzHRW&iz_}s#fe0H?+eS4UX=6RL&os@!!!1sTo9}!Jk~j46#I&OInqd=0p^?T z-dfLhV&+cd*c#cS)vt%&!eG!E#xA+!m^>c>)uaa56~5J~s_(wE?z7hwIRG3}BXazC z>4#u7B*JW|1Bz2Rj`Y&<4O+2zTGY5YlG488aj>M9C7ZV>7aQ6%C8*t$3!jXxzlWpO zKv@T+PD$88M07nyIz{wE_c5#9)*GYiyS3+Kw@6<+Sx?h&@ws^u$Fb`NojsDMGcC zEA%P9a3O~D1cdemupk+IrJgQ(DV~GSKcy4Th-k^(YR$V;w{_P^jojbgz9eQ88W{ah zfl`c3276N|vt%-h&H)7pR{?ItWG*H<4CCcM9R&7$Z46?Ig@jkP%h?aKq3ZbE5EAZwc~VVFx9f1XqmCr;v2tWc;@|5s7(cnv7^Ljk$IRKG|DuVTrji;g<%7-4yL zPjs`F-ICt$7MC9wo1VYww5Y69%;k3Ae$&)1IV0ZMUlEIhLOMQ4e@8VfKHI=o#mxBI zARKgRw@Al7KbuEQ8+g&fCrjnZ>oo^51DQ-4^G$S>_glbXPf!+$$+ZbirCq}}` zJmGe)?%-)E;t5`yO1p2yBp5v#S5PYFd~@%U1}_3;k_xB7B+MIJmWQS$L{_Cfk+9iJ zJRAIMdwPur{P|IgRBwYUQH9iBi68T@EVn)lC3vrwB2mDbI)aL|wnf%EWCJXvWo{}(dowgR zn=S_4NswP%Q^hU~y*MH}h#@`9B2l00`;vap)pE&8CKMvk|pzoiGDD z`)ciw1fpcQX<=7hElBD;6R@ut()aNisFGxLR3P2AK zE~hd>GeYpwitd3{X>q>#4ifw!YLttf++)#$eAl~fLzz3h0Z#%)lP80H7RjQJylIx^ z{YZ@5?gASAMc@_uDOmT9MF#+YD&qHe2d80l`r67z)JJ59m9k_ma6+>W0y z+UIOHOn0;*^Xg2nMZJ5SO_n(?xh-bvi6Y?Jo9WE);k*JI2tq-bNU`TQ(bXAf39bfz zuzdJItZ%6qp5u^VEFkF~=W>)Ms~eywB4nHB(Maz8ug}f0eMwTm(v>3x3296=v>2r) zg{H%$qej>ytJR<^5l8c%Myae;n}t?1+AFxOQRMsISYEJjMd*tIP4kELZtY_^D+gjW ztRVWD$AeYe4eCF*2l38G_o-H@QQ%%?IQQ6&iHF8Li#$x6;z9ZDR`&Cs*^8>QC~N$u zemKe$Rn#Q(g;zFbk{`M0Rfm0r!XVVIw~0@bfK&d0_WYzP{cH^wNO#Loyqawso1WlQ zw|elk%*q@oC!BfRc}Qf@2wY>P=v<&`=0tap6(7DW5OC@G`_oxMlP!-iTH7~jrS7hatkxC(XVG})Ia%cJ~)OS;WF~fuApk$a3 zM~%T>N?o&*(M<6KU*ZZl<_m&;mfl_lklujqtG%P{frX7>d6^LW; zP`Y1fnvA)kO_ioRt8CZx`>hv^@O@EfxPoF_Ln0q?4sfagIbQ40o{7<&)PD1II*qr# zD}dcw#UHvM{6#(mp<8k0u`tpUc*XJO_a2R9lfz@LXSlFCM9)j56Z%KBgiVD|Aj_iF z^X8XW9zhlV0R1W>I9^<~2RyBiDn7$7hE?0{@u?L`K^p09^GsUcItFJT$R20U4|Id) zilnLYa7NY_bu|1JEp0Y$(~r%Kcy2XCW6-96v8v1QF@>rfDUS*+6Kftc318+F???LQj~YdS7kU%_Ez+mUI|_fW>EqwGZBH=y~)Aao#TG zAz$zVc$+ScF99KewW~;QQ5W$+X+!o6^VLOd;k!~mW>SD>OCuU}mHeJH35y=Fktr!G z9&MF9F}E5_B7FW=+(mz~WtAVfV1*9xCt!bsIDxsW8v(oWRXu9N1q857&B+-Ez|Iq^ z3Iuc(vcxezpyT6ePn&>gh@{*Q7 zZetS(#41;X=bl5#M`cTeT=gmP~qw}5?&YQ3Cya6vZYyDIaLUPj~4R#S&t?qM*(rwv?wnM$l z@Vpoj5IDjm=R^nbOwZ9Th_@lg_B#~-!Hiw-2AFikpbP1)J&W%$`t>W%d7gX=1Hx3? z!}8_x7Yy9~a(`Lr=g&O}AiCUu%#uMuT6uyR)imXgN%LmE4NG`dt8~HEGb}<5K&*rR- ziRwKM@aXAMMfgyHFWHUHBN-2_Qx8~n6`;4qlLfceSr_~Cd@11)+f-YY-6A;k*kQ>l zpYIe96G)SotHV0yuB_kGmZ>7VS#MhHIk5OrDhA?@g`?D9NHSD4r8lNByKNfo+@oaj zo4-E<7&&Lrhm&6zF@YF&=d4zpHvXa{t6{7up7w%qTy;ztr;Qo_G9I&q=IX2jaCYE!}?%pJqXKNiKK)Pn!I@rOz zPji@78$X0w1zyCt@DRN|{zbD&BH9}tEeI@C2;(mA7v4xBBnrlJ@SaZ%Dk%(h(bbzLHW*YD{hF;w*T!MhvD=Kq5;>oj{Hg8wnB>}jam<%tR zi0+UCT1UkS`{Y`|Z*RVOO&Ht_MVV5Asp)~T?9^vfM0SrZ!+sjmp}Ch{*P!KdjW5}< z@6hJYDnpy4JSNKCzu;Rh3a;wOsrKF}?0Tk356b}g?Nz&(Jd$H$Bi0Du@I7PHBhh~S z%I5?9LnF~WGbt3XVfacn;WW2qbL)$@zXt|u!9B7~J-p9=AI(7!lgKh-8un^HtJZPS z@h7rvMQdJMj0~PVZ%dPdO{XyRlN-n+l(Q-ahEmv7m3ltTn?R@|YYB`QF*B#9T|qge z#wSLZ>IF?=+np~Pjk$AdZ6d5>FVkHZ?-s?j-6#_VBB#)&uTAV>50n{| zQ7DXE4!^bN9$0f#DTvJJfBbLZYc>jkH<=klvbD=-$q+Zg^%O&2tLwF6yj0~u4*h8B zbxyiE&_NHTcK}I{&(1eb^(Kcp3sWR=0Mciipey&1eh9uqvP;%gIxN`{22h_H6Pvgj zOkMCp?&&E2ASfFHMC6MZh7dOqI7iBvm(kr$9+L90xj$|-*m;0gNPAQsc@N(U*%w7M2!)s(T zV; z$flEFj{*U-xDqX|nVTB-89;Fxqi1LP+a#75=))4Rd|#xDp(iWA1gPi+$dLz9Wb~eB)Y%4U{ zt}QBQy3|Z}0TD(`8U?p^&l=xdmyLjzakh@|9BYCbDD+ZLI{4`VU}bsi1_?ZM@3@Jq zGI5!tgRMMjZltH_u$dc9BM@^;*FS{6IMR%gM1n`0QoukZG87Y>rr05KuC@+>j@f^k zlOIkVvxT@;>g1MIBTE8x9ETevZ-jFdiq^(~L*E&1jK^UN5S{pUn26zEy=_45P(-R- z79}b_h(J|kiMdUq{EkE5;=ElV+-fm7Z?!)8KU97t4#~|0U)kD9Bfe}28N&~BFtmyd z$*5M-CpL_P*H&hE#G7*wCn#hDrf`KB6XD|;o7f#S<(DVa4{NU)pN3}M0OfhTq3>(; znNoype+}p1LPI46C1^^pB?#O?MG{3KAC-636QhKey6Jsa& zS8mVvmqoAu4o~5Cow4hQu&x|FIt@S=-e^w;4G@{il(YHl0Sz#+e;1-6zFr3ILVWz~ z@)&qk!hw%*X)DM9T%#Xvo+xb9VXz_G%n`FlLRf^Zp)#C|k@Pn90DTY#+O2;K-$?Lk z4h1oz-z(UL9SMIoF^jb1V@E&Q0K1{9NeehEpCxjvb?ee)E=C0)!!`ZKnPQ&!R0G5r zOMuR}I-VpV$u*g+-{$3Y!J|BI^KWYSNF#KPG5&;_3I{Nd5eN1E@wmmb&u+N1c~Y({ zgAv@1&OJ$-Yu4OI(7Zhy^j-&-U^?9+CX_IOzQZ#G<{Xau)ZY)j!Wj~0+y-XfOFR(8TjF`Qs6S`4ULI8wo3aMPDxK*wQ5A(#U*d{v}ep~jB{VZe`;%KUZY0l z>VL)xgdC$nPf1H>o77CqzC;3;9rUx?9^W*|Hdy_5q&c{^(Za`hCir=J9ZOJ?TpJWb zQE`-7z2IoWZG2`N5>EJ{-`x64qm4h1|D-`mHjPkJW$!1|T{Rfxs#plZajgF=24SWp z=hrGZ+W{Qc=Cb_jrDQiid=GQ??*P9CET8<(0UnGSIZ4i3hDj_G>Dvs2+Mr~7GJ>s_ z&Su6IB~i$WBDE#oHfg8u%^!G*`4Xl^z;hBC{rtoJnOny&4L&n(1~g>Emhf4X21(CZ3%!__c}eJttgY#ZHgzfjrD;=3qED zZ`N2Yj%C3y@00-_HPtVJEuoQ>DYvWfe0xn`m2`S`3AKi5MO5ayK(j)a#L3eIfxm(s z006v=;FSG;X?ie*CWY}MVije$J9>gVxvDGGd^$ESDeenJthsEc%rxGD)&^C+Dx_4$F)E* zLeup~9I0?GLdc7QM9Tlf1JzoAqQ)geZ}dBvl=bEBel8erTyVx-{~6H&=-fMU{Sin>nW_ zaK|M*Seyr^P^PI*M@*)Q!}a_k$9$DS1}5KJC9q~aJtXtO;7jI?vu~jK7gjA#wCC#H z%a%K-#h6c$p*sm;GYU$aj0evRkG`l9-Xo<2(%(6!{&M{KGl-Od@-UJGY?YcSsR}P| zbwxmNd0zMxT=sC!%3sP>@mDNjVU^wX4!3D+Lh7IGp4W`X%QY`9m?cgz#IqY3h$QOk zSx`0bWaIyyW65FCeJ3FuLGrH4y2hC26n#wrV>Z!gi(I99ZLXr(cLR%*a{UA$rY76J zbtm2;-eoKU(6nD-<_fvi>f-@ndqbvEuYown5LIl*SECT5;LG2vc=mtUvEFEXX9M;t zo1^6KlXkL8OA*eh&M=pZ34zUtB`X*Ow_LlBa7FDYh59uv+v*dL=pStj;m7Ea`v|Fd zx7f0&+?M{1kp*58nQHKZ*PFHIIGl^3J1@6%EemhfPbh-QcKkH}{7ur3dd>oSQApVF zC7)C25A1kbt#XY5z^I?EWp2WfvSgjWPaSRcavh9Y80nkCA5RF-M*$>*)II`}9LOjF zhy^bX%FjmD@J}CWn9?k>VQloH6?%unRy5%enEbjZ&Ny{HZ`44?$WWVmJjsR@m49hK z8J(?|u6x;8w~ef%tvo$9Miv)kVzB7cEG3PC@s$`v~_ z?_(7XWqV)eEHC4EhTq}t;X&-4+5~!?%-tjC1DKechRlE)Y1MHRCH~&~psggul4fO~ zx%o5>(ko}34M>JL;ykore&di@DD%4c<$%YjwbEPKVeMcX>?y1?G@D|AMzzZ*$?H9V zJ5E{!E6i}Y1TJUtMUB}vrL*mfl{TtH($ZABO`MA@Z`~0ct@LA*fdV~<<$4ju74_~iH z6P*q65kyS)aK8QUIrz>S^KPlvw+30Lr`tww!<5^#0KwA~Ulc?(G>s6)rk0%{YrO&B zZDD(Rpd`l*`S?yhi2O?20CA>wK35YXb&SB3vtU4VX`XDH98d>xC&cj{8O3?UQEPpB zZz^=Z--18Qxl#!v@%vlDWh;N~+_N#P%9-)+4K34lnCiiUPPvk{QDZkmo?jLd> zfH6Y0$;?+zczb%3CNfiw6}Y3lmmSm{(C!J6a|yD`GIA7i$|Y10wO}XeFyI@aV}3WA z-bX(|{&{0l*7jFeYtgxSZ(vCN<=Ea<)k(pqVcL)R)>N*l(;$A=s25!eW{6`ep1?$B zVoDM<00>X+s2Qh(u1f#&1SnI3PF-VMu1FSis=n^$|CYj z;OM-ns3iWZPlEUJRQ7sErE!3Xs};Wops zlpPf%>7Qrbo?-bOBPZGQbjbDfr#Z_(!(y|xKo27m@52p8a-Ge;_PHv@=M+Ipci1!M7nWWYU6sNe8y1MWe1W}If<~X0Iz$*;6DjA=n>&$w1xO=ZNlu+SFcH$| zdBtXFCiqp;gx+6-`lUD>ZP2P`0jIT4ahbR*LKWPo^_=;D9ISWZH8JWP8P>=Vgu^2|LMoCLnTY6?JW{5aA+#=Sf|+YxsQh zx&u{W&{UIeNR$=r+Y(H={xR7R1ZVt0y?f4rlx=3(C*p+Ju`b}#WDXngj@-qfEnSdoeT$DH=zAF)h1oa}ZlG$)2ADCfYrRWNXR!UWEKhie_M1{;_f0IGKu4 zLR>R&HIQ_X?qZh@<{KqAm0LEpkEoUl6C0tjqFp?=>3VHGY zs=<1{z=+X}T*h7GyYr&wrObSf$66r+(-mF&bWB{#nIsBFStw;4$=5Rce3$Y#7ip=Z zeANjVG`(JCJAD|by<6P^9e`p$6mYF#XD_qMznn%|*^Rxc-sAUKWG^C8v%2+czC3&* zKj!Vq66MVo#mZn7si;sITi7nrQ7tQYSggRr{q#=jV<5#vC^*t}yq=c=TbWq>4f;Qo()&fSm zG7Z~7d3y8PME1-Z(A?1)MWR9bbGDj;rEfl&IoRPu?~c{RS3DXO!U{+CZg@YWE(IIV zqR~#`ad?bv&tjer2M}+TAh&MACzdn?jc>_RpEQXu>b2tj!CU2>Jrvd$FQS{_Mm682 zc$Yb)UB`i=p{;g;J<#uuTs=gfQ3w>O)Ox3#Q;T^K4M?dnW&0X-#vloYbnCJE{jhP2 z$u~@7u=f*!`D-|=;w=1W@i~^diab5TN0@4isda%IB> z5spwQz|xL67*K=){I?%@HR@c`0Y0qRFc{aFxL7ebHjVR(ZVjMQ>Pj>G|Frkz@l>_# zziZpJnT5+Mi%1}asF-l}ghLR~se%GSs zwcq#myr17W=RNdAn{ zMXSidAf2^qhX=d@*QRI==vIZW$Ow<5amUjJ)g+0I3S7BwQn~wRbEd<^Te53?gj9}O z{uH}ac$kOft>um`W%=!Y`|e82^OG@o6CJ4q-8CBRRg{Fl^v(!niRAiz1@loZiM28j z_Ml^*rY>#u=0Cmn$AHKwrG?kg9eht7?Vz_2nl03?k+#+ur>C81!$%J=eHvad?#LhL ze0NUunXI6TyQ2cfHnGPcHOZ#(Pg_074LYZ4Uyhn|n-i`#F*iDi-FRu7I!0EY8^^W$ zrSZMM=?=x(;pZj%F^PAT31vZ;KpWLHxh_LpMH8A5+ZW{p7fx@fUD9^R)Y1%Cxvpuks z>bF8%U$JltJ*i23fp1-G&?9e;WQ_XId9@|GG@H+GD_QPj+EiDLiTs_4$Bi*BnQyy> zHgY*c_vY>5jhOI@9@Tu?Bm7>HPlI)(@Ngknv^mq);_crR@7*~OpWKSSdS_4c0Y_Pz zmG^eRX)nHp? z6$|(ALTBGMm(Dlj{&B3rjNq|U%P+pSQF&UZts!X0ApqWZx-fUO{^vIy-`jG|E4r39 zKkwStn<{)zUi*gHC*!iPq`SGPMx0m%*WmIa!BzXGi%t!(^XG`&6Kec$qbF5OIefBZ zpHa=b( zvAn5IKl$%{9jvic4cVVGd!B8Q-$KLtoQe)UUMhm5=T2dj-<}Cu@kjMf%^Ih)tbbN! zEG*xqFV5o3Yd@2GOK6+)PWZ+6YdjNb|Ltvt)AvOin9wbtTuuWU_f5 zi_?<6%lj6$SBs*q=zHq`TTh%&*{6z}|g?C~& z@`DGlFN#ze#Jlelbb9SJ2+SKWa?8?@&%7>671dtK@7q+sT=?drX1U&q;ahE80JXKn9uw2$5R|=JV-klt0=Ex*# zsB)}2&3NAxskmsZ8v3!8j&Z@t2QRNG7O0K4$E7RthIS~#J?#vOCmvo^HD1)wCG^?W%Dh}XVRORZ#QHM?z3jFQ>;5+eXemn zl&1Myvl^$<{>4+8nx9F3@G$X21--8};(L<)B;eq*XZjm+2KD)&G~|JSd_nZH)?V+@ zGd%`zGqtaF$y=7z_4Xw`wv9RHp`^H%$HTQGT3am3Q+9AE;8DdX-(yLK8}zT#E$g%3 zJ(%kfcSHnp>l|6OEHzk8KV)UzTOdf6&2zAV4QV*($BH$JmPH6lv}Q z+u|4`_fG7i3`QS^&y4mQ5gOBqdr6D7<*PAjD>k2wFkc-#go@vX_#^pugsj z^DT8(t(54In~ushKD3Xj#I-^k6u!sI55N^yH3Ocd6Yh zysZ|j)MN{eS!mtN!;hPObdo@bCYYF z6m0Qf`vNb$;b}hAj!5D~r&lJtFzu!qY?R3jarJ@i)sI~-$R3n0RPNMtsYI|W>KzVN z>01UqTDV*&5xv$SdnLH*gZdAnZDrq6El5O6v$+*c9sm8v>vH)t1T5~ zx=Z!OzgdgoMk-bKgb-ok5WjQ}=H30{B`3J(Xr~RHhw@e=s&l=05N+7rVit0vYB%D+uJhERT<7@3#xJrK4tdaa;Afciq6~`hm|hb!DZN^Cz#% z3|r3Y-Z@i*;UBL~I8ik$GCHRC!T;hg>(={Mr`~;NBiESTp`5_naE3!Qf9Pq&l7-Gh zBv-;kj;FSYiOkriwAeed;QVS7I$+P`-5uxViqBmdY^TiY&62jRrCbM;KiCbz?b9oqB@n>#&t zAMIqNlC3UW6sLaOLx?n+jx_uv?TrF(?<+hlLH^A zy5)%Wl8XgcMpNB~J<`@&15Yba6)x;e|9JZ>{)yqfm70fV72eC;-o|^Jb#UOav!~>@V6}T?WDYdj zD|s_Wf|mvwxQqGQEcR()57j)w=#g!G^e}v4z8Z_{$8R>QX)D}4AO49t z0awNxFZxU?(og>3(JrNHQ4J014#JHx)mv|hzU(VK;hYveTf^craP_vh*@`t2lIbv8 zXpza0^77sn4bNe#V*jv;`12QKdq2Bf<_Qrz5VWoNou)(<@9KTg^3VH6?^fnhGv*z9 zZ=KbBoGngo$xM{V2uo?;n;Vohf}H&-nmE9>Fdk&Wh7mKNW7W_xWb^PJ%(;Be{cskl}Yg-H@CfaWZqN96umOt)k}BOPDM=HPoQEz4xCn(I39^hBs} zR`+M+u(^CS{nkU@?+ag~q+sGdaX)|i>)1jI;o*VF9lK@)%n#%$9c6iYqIuSt6?@l0 z`t@*A$lIzcyKx2cZ{*X%OF)b0?(5lm&9_@shvq9WxsXWhH@KX% z*4U_3c75^Ww6NCYb_xBWN<-T7veqJI~gz?PK*b zHsUyTn#J$hrdEvIQn&U@l?B=-5%&4z`(i@|&%Zh_D!#kzOog08?Cz+!bA<|@M><37 z$8t`o{rKh`NuGQDQAd4=PxqI!jBVlcGY?nELf&2ykaE0VNFc4Q4;P`1-filXqwr%Q zwaTFK&{3}9UE%?VMsV1+afcWbt1ozd_I?JVsl*QWxVZpA( zW=+XA7@3!UYJQ$fb@QBQdgdU!pQKu$UhYJ>+8@Os?TSljHje6kYz1=EJ`@tS;?nx% zAG0Tj(xjJ&mGP=f!{e3v9y`d)YFHfmvPC+Ag8t|nukdw^h}`AQErN2UM--;+Y2JNc z%%Ifv?1M-tx8tlkYrs{!D8YoFC#)1K+yd{haYd@WPCrJxtfO){P74!m!b91IkXhMx z!2-k8hplV^Rd=;!4EgJ9d3DRxC{5LCOkp;}$w#Z9cw4bZEc5uK{wrp8X^fPN21qYy zK8!Vprq^{5I=+{{ZLnmNb*(a6mri9L`=a%H<%45&=8wfI@cdEUJ0|o_rcFIxWt5XU z&>W?@SX&WjE?>`|8~ zVFWK>E(o)x)FX`gMSj#c<;c6e{@t`UlcZnb73kHK6I^Wa} zj_JT;o@*J!6xY{3ID+HzN zMhTI=5uC{{RlJQSZL99f@4j+f?Io!ZD=V&4C{Sw1pelE8%<4vU#5{*O=i|xCv|bl) z)7RBSJ#=+B7-*pp`>{fJOQEccO~ySX>PiWVL1Xgw-`i3zPmZ({XDD@CTb;-)5W~du z`?O*8j?OTRe#)-aNc*U0IK=y=o`gUd85FPBqo-ms5#JZ_iDxm|g!Bcu-tmJkU-7jb z{&8B)>Ktn(>U(#Bi@LJyD-mZnJS8-Rd(FRwXsGq0zdJjkKSFga=+phW-<{s0X zp`VP}8}I7)?)ArkQD@nxg-O z0I3My{?CqurcKC1>5kA>xfd(>d4;~QT7G+-!c}0tPk#S<`tGYIHTrsDg?ZjBMBf$| zR{6ogmeyk|81P6YyYQ{rZr5=N0jF5LRSWg7{GCDC`Sd5}$_to`s2F&xorD;APtd2$ zt7NuUetb7sc1eGPl6K{&&3Uc3b9ZYl$dn&hDd*hTXWhSjn*Hh?70JBY<6WX({VMqq zC8ZXl7N%Jp?_{O_;jScW!gVMtvckyDXv_D0Y2Jzz!wi}M0pW@tVK0!w-K`^$N3lgZ z=MzZcLrgPX>-$!cyj;^^YM^F0?`bLges)b%^pHNg>yX>ooK}`CA&*bi9q*>QoRiD` zHY)Y7ZtSj;`Pq_{z1yk+&gWEV+8ZqtOqb*ay9XdO=5{5xSO0#OP^^*rpZzx z_TvGSlcuT-Njr4s-dw9LP)nakGW$|vuOKXac~$oh^S$DJ6*a6Brtt=Xh31}D6NMU& zy0$mzOI&xov%vLbmAj%&duU(#bas21Tgo!em`h=Mnx^2sbD?iV7MU6&`CRin2v{BO z2+NNQGkMWVol3%ihHf009|InC8%|FByeX$`{N3rAS0-!Ti@L+cbb8M!_ZHP$I-^ta z0x{$COi2Ivb$eggvwK%8c1#<@STF54Ko%Z*UxGWzR+{H%?tDn>*Uq3%v)I$g^l$c+ z*NPe5xQIEM_cmdF=+^8jhQabjx^`U==oX{(+Jkp4bF=SWTU8qKkE*J-FV64^Vm*6* zDr!tS!uT@P(3hvK)C%RPJu9blBXx5IAM7Y+W*W;1Q?rd1vx+p_zpHmQUt?RNmt%r2 zMMGJfosFpd-Hge;v`QOWb6xx8`>|2SOcPDUnxB3%HBZ^rY?ro6de{^`dzi)Cjr*TJ zdl2(nvcc9m#dDm@34FDH9J|MIM71~8By`cdX1edi80A8r&WD5(7x!>i$nN#|xt0DS z<<3-!BCX=Q+%0``3SL(Yze^n0W3&=4@~wl7GHZUBv(%t>a&T&plgb5ud`49*gx7w= znRl$n;-}!7EW_rij9oh%w9NSmDa7mP?A5c%FqKtf`Kkka!L%vG5}Zv#^C zN?uU~Sp7RXwgzE^yF6pqa&)Q*4`13UCm7WaT0Nhd#~hBSrlPsfenyeGTkSze#cEke z34)OF6P%r`A$WR%wXG!}x?zB)@o+Ok`E4E>Ji`VLulRa+v9m)Ek`WKLb58IWKIkL+ zVTaAmj$~s1uM}r7=n(`#?ap8V5oB#`4gNAPnG_NKev_w_-{djOn>?A{CXak4;u(ne zMIxS@i2u6DGbnEI7`9EGVs4X%CisV+44*c666_{V{(X~&J0<_n$AuB`uQz#oClNof z$&8ALp>oe(0vX;YtmXp{e^xQKY9cT*o{xv7uT z5b;Mhc?>C0k24{ddbFRbH+3CIiTYi8a~|?>Q-@5Hh$rfIyy_;8;oQ`xh~MN9qW;Ad z5b^LI$ozRe(cK~97dCnF8X|sjlgGsn@kDm;H;H&6JAztpjqrrH9SNQr*N6{050Jo4 ziy*8n2!^5;y<)(>&F!BAp#Mcy+|>N55u|j7J6wMp;GmI*!}^-|%{6~nL)ZP+?O)dU zt97xRaOFC*K2gQP%*6vnOcst-=(P?ZI>zMUwtijwC(g?0zj6FlmLBdfb(@u=J8BEv zFzY!%kf$5#3osKb?aYAZCRqMe{wS}XY*?QsVQcB=hK>lfj&7cImR4piE{^L*;Duh< z+0BYu!wO7spMqn~2*O`OuyaBkXYwSh8^$1t0SNj9o9v&)EWwzWqn$gkF;*gDDrO#L z+-hb{mX22F;{28yHn&;2Z6pOaG_5qKjg2wCio1t7>R67uhr1;px}iFWW}X%j>(W1! z96d!yAd!%EH5j5hx?yl?qLMOuHiW-?Wm{VVUJ$G*V>A%1wIPIICt$wm10AJ9rjg8ISW4%DA1RM$c_A>g0#H?-c4}P8dQy)b% zCd|g0)w-SE^4}{TP~P8l{TufA@4Ei~(bvE0{P(Q$@4Egy>-w+XKmD%r-?PrY>-zVs z>+kdTcU^z~JiPwl_21&>#^2ZJzvubyFQ?r4KHAZ(VuK08JIhKf*L zhgU`TtQh_L7~N=KM?Ys(fZZE*^gl+M1xgflxUq=21^W;7-F%Kz;=L#qlx&s(593mbY91nqhBMmdV; zIl#0rrYZqF9~l4I(EAbGaKoKa093W%SLtKnKr4b!!})={f*b4hfUn_p6(b?Lr69owR*gn9mpdSFozX7TM zeubQ^0M7yP8$if0IT{Za=%ee+1FsAS{!^j3*aq@2AmoGuUFS34z73>+++dhNehl&= zAe!TGh%Ea$ncBDZzf%$QOhkys$sE_!oAX|dIDj?*C9Q;B)!7WFK2Lt|6 zFae@}(xGmHr-uAtbU{XI1Y>fj3FNczBQmApq|McoOm!4cG*|Apr<^!PWzsf=x9*$Q6Y!*wh5uet$mT$XHStgf%z4lU|Ir+hV*roCX$AgM@PMz7UoyxaG6=TOy1{X>M38?1 zLSLdihgSk$Btica$Y2+j3fK;@u>LWj<9QT;n0cLN^) z*Z{p=2|Ojp86ejKS^-vreTV`1jvzME5Cdv!5@MqO1fRD+Zb(c)XDiHwoZ^Oo{{k@^ z074GPuLGj7*#SO*wZR_}M&O|?2oSBcI2)gw*n6}M{_|BHc7~Ue+Dv|3v^vH7uP|533#Xl(hIy9;6w0#ALt80 zT+$$;eg**6Lw&u01Aox-6So2gJ;`VS2xAJU5dyYw#enaDhkCEu{s1@wxlsll@{QLA z1dd!05M2{`6?p-%LLD$@eL)B9ADlBF#6Se}7Cj&EX#W&|{0?}C5nUHA1U%HmdHUD+ z3%S9f`Gk42o^Wg{=(2(w0=zAt57>fvaL*pW!FuTR5$g{;*ux(Lc{lKA-k~;F)Hedy z;RSv>@H;?u1l|0-b>8}N`5)ITh`zCGwe9x+nDYl9pHd@LZuWLpn)O)@!y#Rht zfL#P~fx8C?^`dVB8y%3hQjoiVM|02wy#u)@HX&}Pjo&OcJzA~s<(h}o?#&lBmfY6Mc1c= z-dOJwG-nrpN7uF6cz}`OEQ}!+WKw|dL02Aphx(GCI^O{q02cwt0lz|?6yZ1<)DvA} z7UWNmXDs9ybkRP+LI0rFAUxWW6TrU&MDq;mqJ6S{O#!6s{dF9Tp%3)Y`YwS!^ey`S zy?#DEha6jgybN+ZAUYrV7`^_}N&v2ce#S2z@{jf#JzR^{?EtP{Pb>jzVBS%{YA72i z_^u7(j}Vh6AjFJUg*lwS$Ab(p;tBxYK%VaaLQU4c7nlS-W`oDy+~Col=PaCIjBYgW z;~kiR5p>=9TQx2KrUFL^GF3fjz?crkco;JTzyOISSb3NM^8Wdp5=s&**Ew{!xyd?M zdAt7-(KQ57EemIYDEdT`m8k1EI|~O7JA##{i`{uEM@uUwcRLSn&=a@YcqB^@#^^k5 zOL)==5TZr5KOgqOKov1Ou-pDGdz4A!5#$tou574~)*qhT}u3LA4$m$_L9O&O3}u%oqNB|fNO zW+AahSQ=;%*rBlz0pi01GYc(UZV4$-8Bs}YK}pFYQj+3Apd=1Engu~(Wl3?#JtE># WB9hX265{ewvhvchBI0|*#s3TCw|Q0o literal 0 HcmV?d00001 From 9cd4b620b1022634d6aa47a98760cf3515927b4b Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Mon, 25 Mar 2024 11:12:57 -0400 Subject: [PATCH 207/221] =?UTF-8?q?Add=20a=20test=20for=20=E2=80=9Ceval?= =?UTF-8?q?=E2=80=9D=20from=20the=20library?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- alt/scheme/test_rvm.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/alt/scheme/test_rvm.py b/alt/scheme/test_rvm.py index e539894..c488965 100755 --- a/alt/scheme/test_rvm.py +++ b/alt/scheme/test_rvm.py @@ -20,7 +20,7 @@ def vector(chip, **args): def codegen(chip, **args): return run_to_halt(chip, simulator="codegen", interpreter="assembly", **args) def jack(chip, **args): - return run_to_halt(chip, simulator="codegen", interpreter="jack", max_cycles=100000, **args) + return run_to_halt(chip, simulator="codegen", interpreter="jack", **args) return pytest.mark.parametrize("run", [vector, codegen, jack])(f) @parameterize @@ -217,6 +217,18 @@ def test_tty(run): assert output == [ord('0')] +@parameterize +def test_eval(run): + with open("alt/scheme/ribbit/min.scm") as f: + lib = f.readlines() + + program = "\n".join(lib + ["""(eval '(+ 1 2))"""]) + + inspect, output = run(program) + + assert inspect.stack() == [3] + assert output == [] + # # Tests for specific primitives: @@ -380,7 +392,7 @@ def go(xs): return "(define (cons $$x $$y) (rib $$x $$y 0))\n" + go(list(exprs)) -def run_to_halt(program, interpreter, max_cycles=20000, simulator="codegen"): +def run_to_halt(program, interpreter, max_cycles=200000, simulator="codegen"): """Compile and run a Scheme program, then return a function for inspecting the RAM, and a list of words that were written to the TTY port. """ From 96db8c2fd5e5da1f1ea4fa8b79c7781c18e29b69 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Mon, 25 Mar 2024 11:27:12 -0400 Subject: [PATCH 208/221] Run quotient tests only on the Jack interpreter And fix an error --- alt/scheme/test_rvm.py | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/alt/scheme/test_rvm.py b/alt/scheme/test_rvm.py index c488965..cf6ce56 100755 --- a/alt/scheme/test_rvm.py +++ b/alt/scheme/test_rvm.py @@ -13,15 +13,19 @@ SHOW_ALL_LABELS = False -# TODO: put this somewhere common: def parameterize(f): - def vector(chip, **args): - return run_to_halt(chip, simulator="vector", interpreter="assembly", **args) - def codegen(chip, **args): - return run_to_halt(chip, simulator="codegen", interpreter="assembly", **args) - def jack(chip, **args): - return run_to_halt(chip, simulator="codegen", interpreter="jack", **args) - return pytest.mark.parametrize("run", [vector, codegen, jack])(f) + def vector(program, **args): + return run_to_halt(program, simulator="vector", interpreter="jack", **args) + def codegen(program, **args): + return run_to_halt(program, simulator="codegen", interpreter="jack", **args) + def assembly(program, **args): + return run_to_halt(program, simulator="codegen", interpreter="assembly", **args) + return pytest.mark.parametrize("run", [vector, codegen, assembly])(f) + + +def run_jack(program): + return run_to_halt(program=program, simulator="codegen", interpreter="jack") + @parameterize def test_trivial(run): @@ -93,21 +97,21 @@ def test_mul_mixed_signs(run): assert output == [] -@parameterize -def test_quotient(run): +# Note: not implemented in assembly +def test_quotient(): program = "(quotient 123 10)" - inspect, output = run(program) + inspect, output = run_jack(program) assert inspect.stack() == [12] assert output == [] -@parameterize -def test_quotient_mixed_signs(run): - program = several("(quotient 6 -3)", "(quotient -14 -3)", "(* 21 -2)") +# Note: not implemented in assembly +def test_quotient_mixed_signs(): + program = several("(quotient 6 -3)", "(quotient -14 -3)", "(quotient 21 -2)") - inspect, output = run(program) + inspect, output = run_jack(program) assert inspect.stack() == [[-2, 4, -10]] assert output == [] From 3aad0f6d163089dd1fd2ac7e6b16b385e63f338b Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Mon, 25 Mar 2024 11:39:14 -0400 Subject: [PATCH 209/221] =?UTF-8?q?Don=E2=80=99t=20double-quote=20strings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- alt/scheme/inspector.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alt/scheme/inspector.py b/alt/scheme/inspector.py index ac61227..c38c549 100644 --- a/alt/scheme/inspector.py +++ b/alt/scheme/inspector.py @@ -109,7 +109,7 @@ def _obj(self, val, max_depth=10): chars = self._obj(x, max_depth=y) if len(chars) != y: print(f"bad string: {chars, y, z}") - return repr("".join(chr(c) for c in chars)) + return "".join(chr(c) for c in chars) elif z == 4: # vector elems = self._obj(x) if not isinstance(elems, list) or len(elems) != y: From 8f2d85face88f8a1542cdadd47af0c66fd9ba548 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Mon, 25 Mar 2024 11:43:21 -0400 Subject: [PATCH 210/221] =?UTF-8?q?Don=E2=80=99t=20quote=20unrecognized=20?= =?UTF-8?q?ribs;=20fix=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- alt/scheme/inspector.py | 2 +- alt/scheme/test_rvm.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/alt/scheme/inspector.py b/alt/scheme/inspector.py index c38c549..a597834 100644 --- a/alt/scheme/inspector.py +++ b/alt/scheme/inspector.py @@ -96,7 +96,7 @@ def _obj(self, val, max_depth=10): return [car] + cdr else: # In at least one case, this is a "jump" instruction during compilation - return f"({car}, {cdr}, {z})" + return (car, cdr, z) elif z == 1: # proc if 0 <= x < len(PRIMITIVES): return f"proc({PRIMITIVES[x]})" diff --git a/alt/scheme/test_rvm.py b/alt/scheme/test_rvm.py index cf6ce56..8808ab8 100755 --- a/alt/scheme/test_rvm.py +++ b/alt/scheme/test_rvm.py @@ -293,7 +293,7 @@ def test_field0_set(run): inspect, output = run(program) - assert inspect.stack() == [([10, 8, 9], 10)] + assert inspect.stack() == [([10, 8, 9], 10, 0)] assert output == [] @@ -330,7 +330,7 @@ def test_field2_set(run): inspect, output = run(program) - assert inspect.stack() == [(11, 11)] + assert inspect.stack() == [(11, 11, 0)] assert output == [] From 5e846fc0e3b3c6a7ad47a4f3b486bf78af005a42 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Mon, 25 Mar 2024 11:43:52 -0400 Subject: [PATCH 211/221] Remove dead code --- alt/scheme/test_rvm.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/alt/scheme/test_rvm.py b/alt/scheme/test_rvm.py index 8808ab8..fd5fd60 100755 --- a/alt/scheme/test_rvm.py +++ b/alt/scheme/test_rvm.py @@ -406,20 +406,6 @@ def run_to_halt(program, interpreter, max_cycles=200000, simulator="codegen"): instrs, symbols, stack_loc, pc_loc, next_rib_loc, interp_loop_addr, halt_loop_addr = rvm.assemble(encoded, interpreter, True) - # asm = AssemblySource() - - # if interpreter == "assembly": - # asm = rvm.asm_interpreter() - # elif interpreter == "jack": - # asm = rvm.jack_interpreter() - - # rvm.decode(encoded, asm) - - # instrs, symbols, _ = big.assemble(asm.lines, min_static=None, builtins=rvm.BUILTINS) - - # for k, v in symbols.items(): - # print(k, v) - computer = nand.syntax.run(big.BigComputer, simulator=simulator) computer.init_rom(instrs) From a9080d6f0d08af70420e35c98ef663a031fc566e Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Mon, 25 Mar 2024 12:12:20 -0400 Subject: [PATCH 212/221] Add a link to scheme --- alt/README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/alt/README.md b/alt/README.md index 04074c8..7eaf67d 100644 --- a/alt/README.md +++ b/alt/README.md @@ -43,6 +43,12 @@ local variables and expression evaluation, reserving the stack only for subrouti [alt/reduce.py](reduce.py) adds an optimization phase after parsing and before the normal compiler runs, which replaces certain function calls with lower-overhead "reduced" alternatives. + +## Alternative languages + +[alt/scheme](scheme/) provides a compiler and REPL for the Scheme language (circa R4RS), using the "big" architecture. + + ## Results | Location | Nands | ROM size | Cycles per frame | Cycles for init | From 5a1358ba7ef93ca05d0e12c9f065e4fba310c539 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Mon, 25 Mar 2024 12:25:00 -0400 Subject: [PATCH 213/221] Keep original rom size when overwriting the contents --- nand/codegen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nand/codegen.py b/nand/codegen.py index 59956e0..13274e7 100644 --- a/nand/codegen.py +++ b/nand/codegen.py @@ -569,7 +569,7 @@ def init_rom(self, instructions): size, # @size (which is the address of this instruction) 0b111_0_000000_000_111, # JMP ] - self._rom = contents + self._rom[:len(contents)] = contents # TODO: surprisingly, this is not faster (no apparent effect): # self._rom = array.array('H', contents) From 90ef6becfc9534d0b3a1bc3b5aa60b2cf9d332c0 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Tue, 26 Mar 2024 12:55:16 -0400 Subject: [PATCH 214/221] =?UTF-8?q?Add=20=E2=80=94scale=20parameter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- computer.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/computer.py b/computer.py index 5c41927..8a2346e 100755 --- a/computer.py +++ b/computer.py @@ -45,6 +45,7 @@ parser.add_argument("--max-fps", action="store", type=int, help="Experimental! (VM/Jack-only) pin the game loop to a fixed rate, approximately (in games that use Sys.wait).\nMay or may not work, depending on the translator.") # TODO: "--max-cps"; limit the clock speed directly. That will allow different chips to be compared (in a way). # TODO: "--headless" with no UI, with Keyboard and TTY connected to stdin/stdout +parser.add_argument("--scale", action="store_true", help="Scale the display by a whole number multiplier to approximately fill the screen.") def main(platform=USER_PLATFORM): args = parser.parse_args() @@ -62,7 +63,8 @@ def main(platform=USER_PLATFORM): src_map=src_map if args.trace else None, is_in_wait=in_function_pred(None if args.no_waiting else wait_addresses), max_fps=args.max_fps, - is_in_halt=in_function_pred(halt_addresses)) + is_in_halt=in_function_pred(halt_addresses), + scale=args.scale) def load(platform, path, print_asm=False, no_waiting=False): @@ -166,16 +168,17 @@ def load(platform, path, print_asm=False, no_waiting=False): """ class KVM: - def __init__(self, title, width, height): + def __init__(self, title, width, height, scale=False): self.width = width self.height = height pygame.init() flags = 0 - # flags = pygame.FULLSCREEN - # pygame.SCALED requires 2.0.0 - flags |= pygame.SCALED + # flags |= pygame.FULLSCREEN + if scale: + # pygame.SCALED requires 2.0.0 + flags |= pygame.SCALED self.screen = pygame.display.set_mode((width, height), flags=flags) pygame.display.set_caption(title) @@ -224,11 +227,11 @@ def update_display(self, get_pixel): pygame.display.flip() -def run(program, chip, name="Nand!", simulator="codegen", src_map=None, is_in_wait=(lambda _: False), max_fps=None, is_in_halt=(lambda _: False)): +def run(program, chip, name="Nand!", simulator="codegen", src_map=None, is_in_wait=(lambda _: False), max_fps=None, is_in_halt=(lambda _: False), scale=False): computer = nand.syntax.run(chip, simulator=simulator) computer.init_rom(program) - kvm = KVM(name, 512, 256) + kvm = KVM(name, 512, 256, scale=scale) last_cycle_time = last_event_time = last_display_time = last_frame_time = now = time.monotonic() was_in_sys_wait = False From 31d35868d597634cd7d0bf38a112816d162cbea5 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Tue, 26 Mar 2024 13:33:29 -0400 Subject: [PATCH 215/221] Halt when the heap is full --- alt/scheme/Interpreter.jack | 18 ++++++++++++++++++ alt/scheme/Rib.jack | 12 ++++++++---- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/alt/scheme/Interpreter.jack b/alt/scheme/Interpreter.jack index 91f3e5c..0ad4bef 100644 --- a/alt/scheme/Interpreter.jack +++ b/alt/scheme/Interpreter.jack @@ -956,6 +956,10 @@ class Interpreter { /** Allocate a rib on the heap, filling in the three fields. */ function Rib alloc(int x, int y, int z) { var Rib r; + + // Note: makes this not a leaf function, but most callers have already inlined it anyway + do Interpreter.checkHeap(); + let r = nextRib; let r[0] = x; let r[1] = y; @@ -964,6 +968,20 @@ class Interpreter { return r; } + /** + Make sure there's space to allocate a rib, otherwise halt. + + TODO: make space by collecting garbage. + */ + function void checkHeap() { + if (~Rib.isRib(nextRib)) { + do Interpreter.halt(); + } + else { + return; + } + } + function void push(int obj) { // let stack = Interpreter.alloc(obj, stack, 0); // pair-type diff --git a/alt/scheme/Rib.jack b/alt/scheme/Rib.jack index 7d31958..7677f3f 100644 --- a/alt/scheme/Rib.jack +++ b/alt/scheme/Rib.jack @@ -3,16 +3,20 @@ class Rib { /** TEMP: for now every rib pointer is definitely above ROM_BASE (4096), so any value smaller - than that is considered an un-boxed int. We'll also treat negative values in teh same range + than that is considered an un-boxed int. We'll also treat negative values in the same range as raw ints. This is all bogus. Need to implement a proper tagging scheme so we can use larger int values (i.e. 15 bits.) */ + // TODO: get the compiler to inline this function boolean isRib(int obj) { return (obj < -4095) | (obj > 4095); } - method int x() { - return x; - } + // TODO: get the compiler to inline this, so we can actually afford to use it + // method int x() { + // return x; + // } + + // TODO: y(), z(), setX/Y/Z()? } From a092020b8093296d66396d20308c1b07484600d8 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Tue, 26 Mar 2024 13:34:32 -0400 Subject: [PATCH 216/221] Default to coarse logging (when is this used?) --- alt/scheme/rvm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alt/scheme/rvm.py b/alt/scheme/rvm.py index 16ea55a..04a044e 100755 --- a/alt/scheme/rvm.py +++ b/alt/scheme/rvm.py @@ -32,7 +32,7 @@ """Log every CPU instruction (in addition to COARSE and FINE logging.)""" DEFAULT_PRINT_ASM = False -DEFAULT_TRACE_LEVEL = TRACE_FINE +DEFAULT_TRACE_LEVEL = TRACE_COARSE def run(program, interpreter, simulator, print_asm=DEFAULT_PRINT_ASM, trace_level=DEFAULT_TRACE_LEVEL, verbose_tty=True): From 6cf374aa1cd27b32dc8b5cf5d799e623e5091f99 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Wed, 27 Mar 2024 09:58:08 -0400 Subject: [PATCH 217/221] Update comments --- alt/reg.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/alt/reg.py b/alt/reg.py index 2a8b274..d9e3eaf 100755 --- a/alt/reg.py +++ b/alt/reg.py @@ -46,7 +46,7 @@ of a function or method and invoking it later): - Jack.symbol(): address of the given label. -- Jack.invoke(ptr, ...): call the function/method referred to by the pointer. +- Jack.invoke(ptr): call the function/method referred to by the pointer. For example, this code sequence: @@ -54,13 +54,13 @@ var int fptr; // the type doesn't matter let fooPtr = Jack.symbol("main.foo"); ... -do Jack.invoke(fooPtr, "bar"); +do Jack.invoke(fooPtr); ``` has the same effect as this simple call: ``` -do Main.foo("bar"); +do Main.foo(); ``` """ @@ -88,7 +88,7 @@ PRINT_LIVENESS = False """Print each function before assigning variables to registers. Each statement is annotated with the set of variables which are 'live' at the beginning of -that statement. A live variable contains a value that will be used later; it must nt be overwritten +that statement. A live variable contains a value that will be used later; it must not be overwritten at that point.""" PRINT_IR = True @@ -101,7 +101,7 @@ # IR # # A simplified AST for statements and expressions: -# - no expressions; each statement does a single calculation +# - no nested expressions; each statement does a single calculation # - if and while are still here, to simplify control-flow analysis # - separate representations for local variables, which are not at first assigned to a specific # location, and other variables, for which the location is fixed in the symbol table. From ac004c727a4589c4b84d21f7a23dcb0a2d94760c Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Wed, 27 Mar 2024 09:58:34 -0400 Subject: [PATCH 218/221] Disable logging of IR --- alt/reg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alt/reg.py b/alt/reg.py index d9e3eaf..139b31e 100755 --- a/alt/reg.py +++ b/alt/reg.py @@ -91,7 +91,7 @@ that statement. A live variable contains a value that will be used later; it must not be overwritten at that point.""" -PRINT_IR = True +PRINT_IR = False """Print each function immediately before emitting code. Variables have been assigned to concrete locations, and each statement represents a unit of work for which code can be generated directly. From 1a25ea98be727e91b87241f5fb2eadbe2e409a85 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Wed, 27 Mar 2024 10:11:05 -0400 Subject: [PATCH 219/221] More comment fixes --- alt/reg.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/alt/reg.py b/alt/reg.py index 139b31e..f3b1d31 100755 --- a/alt/reg.py +++ b/alt/reg.py @@ -46,22 +46,18 @@ of a function or method and invoking it later): - Jack.symbol(): address of the given label. -- Jack.invoke(ptr): call the function/method referred to by the pointer. +- Jack.invoke(ptr): call the function referred to by the pointer. For example, this code sequence: -``` -var int fptr; // the type doesn't matter -let fooPtr = Jack.symbol("main.foo"); -... -do Jack.invoke(fooPtr); -``` + var int fooPtr; // the type doesn't matter + let fooPtr = Jack.symbol("main.foo"); + ... + do Jack.invoke(fooPtr); has the same effect as this simple call: -``` -do Main.foo(); -``` + do Main.foo(); """ From 4646f65854914720c43e3a7cd239b1f7339483ec Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Wed, 27 Mar 2024 10:30:02 -0400 Subject: [PATCH 220/221] Increase max cycles --- alt/scheme/test_rvm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alt/scheme/test_rvm.py b/alt/scheme/test_rvm.py index fd5fd60..6b531ad 100755 --- a/alt/scheme/test_rvm.py +++ b/alt/scheme/test_rvm.py @@ -396,7 +396,7 @@ def go(xs): return "(define (cons $$x $$y) (rib $$x $$y 0))\n" + go(list(exprs)) -def run_to_halt(program, interpreter, max_cycles=200000, simulator="codegen"): +def run_to_halt(program, interpreter, max_cycles=250000, simulator="codegen"): """Compile and run a Scheme program, then return a function for inspecting the RAM, and a list of words that were written to the TTY port. """ From 8368b5e5c374f0a47da2d0b8ff3bdb0da4c59668 Mon Sep 17 00:00:00 2001 From: Moss Prescott Date: Wed, 27 Mar 2024 11:23:06 -0400 Subject: [PATCH 221/221] Add an interactive example --- alt/scheme/Interpreter.jack | 29 ++++++++++++++++++++--------- alt/scheme/example/game.scm | 35 +++++++++++++++++++++++++++++++++++ alt/scheme/io.scm | 1 + 3 files changed, 56 insertions(+), 9 deletions(-) create mode 100644 alt/scheme/example/game.scm diff --git a/alt/scheme/Interpreter.jack b/alt/scheme/Interpreter.jack index 0ad4bef..4f42380 100644 --- a/alt/scheme/Interpreter.jack +++ b/alt/scheme/Interpreter.jack @@ -298,7 +298,7 @@ class Interpreter { return; } - /* arg1 :: x y -- x") # i.e. "drop" */ + /* arg1 :: x y -- x) # i.e. "drop" */ function void handleArg1() { let stack = stack[1]; @@ -664,22 +664,33 @@ class Interpreter { return; } - /* peek :: x -- RAM[x] */ + /* peek :: x -- RAM[x] + + Note: the address is limited to the range of (tagged) int. It certainly can address low RAM, + including the screen buffer and the location for the keyboard/TTY. + */ function void handlePeek() { - var int x, y, z; - var Rib tmp; + var int x; + var Array tmp; - // TODO - do Interpreter.halt(); + let tmp = 0; + + let x = stack[0]; + let stack[0] = tmp[x]; + + return; } - /* poke :: x y -- y (and write the value y at RAM[x]) */ - function void handlePoke() { + /* poke :: x y -- y (and write the value y at RAM[x]) + + Note: the address is limited to the range of (tagged) int. It certainly can address low RAM, + including the screen buffer and the location for the keyboard/TTY. + */ + function void handlePoke() { var int y; var Array tmp; // Note: one rib becomes garbage: - // TODO: inline it // let y = Interpreter.pop(); let y = stack[0]; let stack = stack[1]; diff --git a/alt/scheme/example/game.scm b/alt/scheme/example/game.scm new file mode 100644 index 0000000..2888bfa --- /dev/null +++ b/alt/scheme/example/game.scm @@ -0,0 +1,35 @@ +;; Move a character around the screen, controlled by the arrow keys. +;; +;; Ok, so it's not much of a game. And there's a fair amount of flicker. +;; And it consumes memory just waiting for a key to be pressed, because +;; it doesn't use the optimized, but terminal-oriented getchar primitive. +;; +;; Requires min.scm and io.scm + +(define x 39) +(define y 12) + +(define (game) + (let ((c (peek 4095))) + (cond + ((= c 130) + (drawchar x y 0) + (set! x (- x 1)) + (drawchar x y 42)) + ((= c 131) + (drawchar x y 0) + (set! y (- y 1)) + (drawchar x y 42)) + ((= c 132) + (drawchar x y 0) + (set! x (+ x 1)) + (drawchar x y 42)) + ((= c 133) + (drawchar x y 0) + (set! y (+ y 1)) + (drawchar x y 42)) + ) + (game))) + +(drawchar x y 42) +(game) diff --git a/alt/scheme/io.scm b/alt/scheme/io.scm index 3420f79..95fca43 100644 --- a/alt/scheme/io.scm +++ b/alt/scheme/io.scm @@ -8,6 +8,7 @@ ;; screen buffer memory faster than interpreted schems could and without allocation. +(define peek (rib 20 0 1)) (define poke (rib 21 0 1)) ;; (define screen 2048)

    $3V!u2JNldnUEqCL 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 z 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 z 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