Пример #1
0
def _pop(register):
    """
    Pop off the stack, manipulating both TOS and PSP.
    """
    ucode = assemble(SET, register, Z)
    ucode += assemble(SET, Z, POP)
    return ucode
Пример #2
0
def POPRSP(register):
    """
    Pop from RSP.
    """
    ucode = assemble(SET, register, [Y])
    ucode += assemble(ADD, Y, 0x1)
    return ucode
Пример #3
0
def _push(register):
    """
    Push onto the stack, manipulating both TOS and PSP.
    """
    ucode = assemble(SET, PUSH, Z)
    ucode += assemble(SET, Z, register)
    return ucode
Пример #4
0
def if_else(target, otherwise):
    """
    Add a call to a block directly after an if statement. The block will only
    be executed if the if block was not executed.
    """

    print "Making if/else", hex(target), hex(otherwise)

    # We don't know the size of the block we wish to jump over quite yet;
    # let's figure that out first.
    ifblock = call(target)

    # Let's also make the else block.
    elseblock = call(otherwise)

    # Same as before, but with a twist: At the end of the ifblock, we're going
    # to jump over the else block in the same style.
    ifblock += assemble(ADD, PC, len(elseblock) // 2)

    # Now assemble as before. First, the test.
    ucode = assemble(IFE, 0x0, POP)
    # Now we jump over the block...
    ucode += assemble(ADD, PC, len(ifblock) // 2)
    # And insert the call to the block.
    ucode += ifblock
    # Now the else block.
    ucode += elseblock
    # All done!
    return ucode
Пример #5
0
def PUSHRSP(register):
    """
    Push onto RSP.
    """
    ucode = assemble(SUB, Y, 0x1)
    ucode += assemble(SET, [Y], register)
    return ucode
Пример #6
0
def binop(op):
    """
    Compile a binary operation.
    """

    opcode = binops[op]

    ucode = assemble(SET, A, POP)
    ucode += assemble(opcode, PEEK, A)
    return ucode
Пример #7
0
def read(register):
    """
    Get a byte from the keyboard and put it in the given register.

    This blocks.
    """

    ucode = assemble(SET, register, [0x9010])
    ucode = until(ucode, (IFE, register, 0x0))
    ucode += assemble(SET, [0x9010], 0x0)
    ucode += assemble(SET, PC, POP)
    return ucode
Пример #8
0
def bootloader(start):
    """
    Set up stacks and registers, and then jump to a starting point. After
    things are finished, pop some of the stack to registers, and halt with an
    illegal opcode.
    """

    # First things first. Set up the call stack. Currently hardcoded.
    ucode = assemble(SET, Z, 0xd000)
    # Hardcode the location of the tail, and call.
    ucode += call(start)
    # And we're off! As soon as we come back down, pop I and J so we can see
    # them easily.
    ucode += assemble(SET, I, POP)
    ucode += assemble(SET, J, POP)
    # Finish off with an illegal opcode.
    ucode += pack(">H", 0x0)
    return ucode
Пример #9
0
def ret():
    """
    Return to the caller.

    It's totally possible to return to lala-land with this function. Don't use
    it if you are not confident that the caller actually pushed a return
    location onto the return/call stack.
    """

    return assemble(SET, PC, [Z])
Пример #10
0
def call(target):
    """
    Call a subroutine.

    This call is built to be position-independent. The return value pushed
    onto the stack is calculated at runtime, and the return/call stack is
    managed by this function, so no effort is required beyond ensuring that
    the target is already fixed in location.

    Safety not guaranteed; you might not ever come back.
    """

    # Make space on the call stack.
    ucode = assemble(SUB, Z, 0x1)
    # Hax. Calculate where we currently are based on PC, and then expect that
    # we will take a certain number of words to make our actual jump.
    # Grab PC into a GPR. Note that PC increments before it's grabbed here, so
    # this instruction doesn't count towards our total.
    ucode += assemble(SET, A, PC)
    # 0x0+1: Add our offset to PC in A.
    ucode += assemble(ADD, A, 0x4)
    # 0x1+1: Push our offset into the ret/call stack on Z.
    ucode += assemble(SET, [Z], A)
    # 0x2+2: Make our call, rigged so that it will always be two words.
    ucode += assemble(SET, PC, Absolute(target))
    # 0x4 is business as usual. Whereever we were from, we *probably* wanna
    # decrement Z again.
    ucode += assemble(ADD, Z, 0x1)
    return ucode
Пример #11
0
def if_alone(target):
    """
    Consider the current value on the stack. If it's true, then execute a
    given code block. Otherwise, jump to the next code block.
    """

    print "Making if", hex(target)

    # We don't know the size of the block we wish to jump over quite yet;
    # let's figure that out first.
    block = call(target)

    # Our strategy is to put together a small jump over the block if the value
    # is false. If it's true, then the IFE will jump over the jump. Double
    # negatives fail to lose again!
    ucode = assemble(IFE, 0x0, POP)
    # Now we jump over the block...
    ucode += assemble(ADD, PC, len(block) // 2)
    # And insert the call to the block.
    ucode += block
    # All done!
    return ucode
Пример #12
0
def rot():
    ucode = assemble(SET, A, POP)
    ucode += assemble(SET, B, POP)
    ucode += assemble(SET, C, POP)
    ucode += assemble(SET, PUSH, B)
    ucode += assemble(SET, PUSH, A)
    ucode += assemble(SET, PUSH, C)
    return ucode
Пример #13
0
    def asm(self, name, ucode, flags=None):
        """
        Write an assembly-level word into the core.

        Here's what the word looks like:

        |prev|len |name|asm |NEXT|
        """

        print "Adding assembly word %s" % name

        self.create(name, flags)
        self.space.write(ucode)
        self.space.write(assemble(SET, PC, self.asmwords["next"]))
Пример #14
0
    def bootloader(self):
        """
        Set up the bootloader.
        """

        self.space.write(assemble(SET, Y, 0xD000))
        self.space.write(assemble(SET, J, 0x5))
        self.space.write(assemble(SET, PC, [J]))

        # Allocate space for the address of QUIT.
        self.space.write("\x00\x00")

        # Allocate space for STATE.
        self.STATE = self.space.tell()
        self.space.write("\x00\x00")

        # And HERE.
        self.HERE = self.space.tell()
        self.space.write("\x00\x00")

        # And LATEST, too.
        self.LATEST = self.space.tell()
        self.space.write("\x00\x00")

        # Don't forget FB.
        self.FB = self.space.tell()
        self.space.write("\x80\x00")

        # NEXT. Increment IP and move through it.
        ucode = assemble(ADD, J, 0x1)
        ucode += assemble(SET, PC, [J])
        self.prim("next", ucode)

        # EXIT. Pop RSP into IP and then call NEXT.
        ucode = POPRSP(J)
        ucode += assemble(SET, PC, self.asmwords["next"])
        self.prim("exit", ucode)

        # ENTER. Save IP to RSP, dereference IP to find the caller, enter the
        # new word, call NEXT.
        ucode = PUSHRSP(J)
        ucode += assemble(SET, J, [J])
        ucode += assemble(SET, PC, self.asmwords["next"])
        self.prim("enter", ucode)
Пример #15
0
def builtin(word):
    """
    Compile a builtin word.
    """

    try:
        i = int(word)
        ucode = assemble(SET, PUSH, i)
        return ucode
    except ValueError:
        pass

    if word in prims:
        return prims[word]()

    if word in binops:
        return binop(word)

    raise Exception("Don't know builtin %r" % word)
Пример #16
0
def memcpy():
    """
    Copy A bytes from B to C. Clobbers A.

    The copy is made back to front. No overlapping check is done.
    """

    preamble = assemble(ADD, B, A)
    preamble += assemble(ADD, C, A)
    # Top of the loop.
    ucode = assemble(SUB, A, 0x1)
    ucode += assemble(SUB, B, 0x1)
    ucode += assemble(SUB, C, 0x1)
    ucode += assemble(SET, [C], [B])
    ucode = until(ucode, (IFN, A, 0x0))
    # And return.
    ucode += assemble(SET, PC, POP)
    return preamble + ucode
Пример #17
0
    def thread(self, name, words, flags=None):
        """
        Assemble a thread of words into the core.

        Here's what a thread looks like:

        |prev|len |name|ENTER|word|EXIT|
        """

        print "Adding Forth thread %s" % name

        self.create(name, flags)
        # ENTER/DOCOL bytecode.
        ucode = assemble(SET, PC, self.asmwords["enter"])
        self.space.write(ucode)
        for word in words:
            if isinstance(word, int):
                self.space.write(pack(">H", word))
            elif word in self.codewords:
                self.space.write(pack(">H", self.codewords[word]))
            else:
                raise Exception("Can't reference unknown word %r" % word)
        self.space.write(pack(">H", self.asmwords["exit"]))
Пример #18
0
def memcmp():
    """
    Put a length in A, two addresses in B and C, and fill A with whether
    they match (non-zero) or don't match (zero).
    """

    # Save X.
    preamble = assemble(SET, PUSH, X)
    preamble += assemble(SET, X, 0x0)
    preamble += assemble(ADD, B, A)
    preamble += assemble(ADD, C, A)
    # Top of the loop.
    ucode = assemble(SUB, B, 0x1)
    ucode += assemble(SUB, C, 0x1)
    ucode += assemble(IFE, [B], [C])
    ucode += assemble(BOR, X, 0xffff)
    ucode = until(ucode, (IFN, A, 0x0))
    ucode += assemble(SET, A, X)
    ucode += assemble(XOR, A, 0xffff)
    # Restore X.
    ucode += assemble(SET, X, POP)
    ucode += assemble(SET, PC, POP)
    return preamble + ucode
Пример #19
0
def dup():
    ucode = assemble(SET, A, PEEK)
    ucode += assemble(SET, PUSH, A)
    return ucode
Пример #20
0
 def test_set_register_literal(self):
     expected = "\x7c\x01\x00\x30"
     self.assertEqual(expected, assemble(SET, A, 0x30))
Пример #21
0
def rdrop():
    return assemble(ADD, X, 0x1)
Пример #22
0
def r_at():
    return assemble(SET, PUSH, [X])
Пример #23
0
def drop():
    return assemble(ADD, SP, 0x1)
Пример #24
0
def to_r():
    ucode = assemble(SUB, X, 0x1)
    ucode += assemble(SET, [X], POP)
    return ucode
Пример #25
0
def swap():
    ucode = assemble(SET, A, POP)
    ucode += assemble(SET, B, POP)
    ucode += assemble(SET, PUSH, A)
    ucode += assemble(SET, PUSH, B)
    return ucode
Пример #26
0
 def test_jsr_literal(self):
     expected = "\x7c\x10\x00\x42"
     self.assertEqual(expected, assemble(JSR, 0x42))
Пример #27
0
def over():
    ucode = assemble(SET, A, SP)
    ucode += assemble(SET, PUSH, [A + 0x1])
    return ucode
Пример #28
0
            elif word in self.codewords:
                self.space.write(pack(">H", self.codewords[word]))
            else:
                raise Exception("Can't reference unknown word %r" % word)
        self.space.write(pack(">H", self.asmwords["exit"]))


ma = MetaAssembler()

# Deep primitives.

ma.prim("read", read(A))
ma.prim("write", write(A))

# Top of the line: Go back to the beginning of the string.
ucode = assemble(SET, B, 0x0)
ucode += assemble(SET, C, ma.workspace)
# Read a character into A.
ucode += call(ma.asmwords["read"])
ucode += assemble(SET, [C], A)
ucode += assemble(ADD, B, 0x1)
ucode += assemble(ADD, C, 0x1)
# If it's a space, then we're done. Otherwise, go back to reading things from
# the keyboard.
ucode = until(ucode, (IFN, 0x20, [C]))
ucode += assemble(SET, C, ma.workspace)
ma.prim("word", ucode)

preamble = assemble(SET, C, 0x0)
ucode = assemble(MUL, C, 10)
ucode += assemble(SET, X, [A])
Пример #29
0
def write(register):
    """
    Write a byte to the framebuffer.

    The register needs to not be PEEK or POP, because SP is modified when this
    function is entered.

    Self-modifying code is used to track the cursor in the framebuffer.
    """

    # Save Y.
    ucode = assemble(SET, PUSH, Y)
    # Save Z.
    ucode += assemble(SET, PUSH, Z)
    # Save the data that we're supposed to push.
    ucode = assemble(SET, PUSH, register)
    # Do some tricky PC manipulation to get a bareword into the code, and
    # sneak its address into Z.
    ucode += assemble(SET, Z, PC)
    ucode += assemble(IFE, 0x0, 0x0)
    ucode += "\x80\x00"
    ucode += assemble(ADD, Z, 0x1)
    # Dereference the framebuffer.
    ucode += assemble(SET, Y, [Z])
    # Write to the framebuffer.
    ucode += assemble(SET, [Y], POP)
    # Advance the framebuffer.
    ucode += assemble(ADD, [Z], 0x1)
    # If the framebuffer has wrapped, wrap the pointer.
    ucode += assemble(IFG, 0x8200, [Z])
    ucode += assemble(SUB, [Z], 0x200)
    # Restore registers and leave.
    ucode += assemble(SET, Z, POP)
    ucode += assemble(SET, Y, POP)
    ucode += assemble(SET, PC, POP)
    return ucode
Пример #30
0
 def test_set_push_z(self):
     expected = "\x15\xa1"
     self.assertEqual(expected, assemble(SET, PUSH, Z))