def restart_or_quit(): assert pc() & 0xFF == 0, "restart_or_quit must be placed at the start of a page" label("forth.restart-or-quit") bra([W_lo]) # 6 ble(pc() + 1) # 7 # 8 happens in start of thread again label(".quit") ld(hi("forth.exit"), Y) # 9 C("jmp forth.exit.from-failed-test") jmp(Y, lo("forth.exit.from-failed-test")) # 10
def shift(vtmp): """Place all of the code required for 2*, LSHIFT, RSHIFT and 2/ Needs a page to itself. """ # Customized restart or quit trampoline which loads Y and X so that # they point at the data stack. This saves space at no runtime cost. assert pc( ) & 0xFF == 0, "restart_or_quit must be placed at the start of a page" label("forth.restart-or-quit") ble(pc() + 3) # 6 ld(data_stack_page, Y) # 7 bra([W_lo]) ld([data_stack_pointer], X) # 8; nop for purposes of .quit label(".quit") ld(hi("forth.exit"), Y) # 9 C("jmp forth.exit.from-failed-test") jmp(Y, lo("forth.exit.from-failed-test")) # 10 # 11, overlap with whatever comes next - hopefully not a branch or jump! _two_times() offset_start = pc() _lshift() offset_of_shift_by_8 = pc() - offset_start _lshift__amount_eq_8() offset_of_shift_by_gt_8 = pc() - offset_start _lshift__amount_gt_8() offset_of_shift_by_lt_8 = pc() - offset_start _lshift__amount_lt_8() rshift_offset_start = pc() _rshift() assert pc() - rshift_offset_start <= offset_of_shift_by_8 fillers(until=(rshift_offset_start + offset_of_shift_by_8) & 255) _rshift__amount_eq_8() assert pc() - rshift_offset_start <= offset_of_shift_by_gt_8 fillers(until=(rshift_offset_start + offset_of_shift_by_gt_8) & 255) _rshift__amount_gt_8() assert pc() - rshift_offset_start <= offset_of_shift_by_lt_8 fillers(until=(rshift_offset_start + offset_of_shift_by_lt_8) & 255) _rshift__amount_lt_8() _shift_entry( offset_to_amount_eq_8=offset_of_shift_by_8, offset_to_amount_gt_8=offset_of_shift_by_gt_8, offset_to_amount_lt_8=offset_of_shift_by_lt_8, ) _left_shift_by_n() _right_shift_by_n(vtmp) _two_div(vtmp)
def next3_ram_rom(): """NEXT3 to use when in RAM->ROM mode""" label("forth.next3.ram-rom-mode") adda(-(cost_of_next3_ram_rom / 2)) # 1 ld([IP_hi], Y) # 2 C("W <- [IP]") ld([IP_lo], X) # 3 ld([Y, X]) # 4 st([W_lo]) # 5 ld([IP_lo]) # 6 adda(1) # 7 ld(AC, X) # 8 ld([Y, X]) # 9 # Increment IP st([W_hi]) # 10 C("IP <- IP + 2") ld([IP_lo]) # 11 adda(2) # 12 beq(pc() + 5) # 13 st([IP_lo]) # 14 REENTER(14) label(".page-boundary") ld([IP_hi]) # 15 adda(1) # 16 st([IP_hi]) # 17 REENTER(17)
def wrapper(*args, **kwargs): if asm.pc() & 0xFF < 0xFF: # We haven't run out of space yet - Let's keep going! # Record this call for future playback _assembler_call_buffer.append((function, args, kwargs)) else: # Oh no - this thread definition crosses a page boundary # Restore the asm.py state to what it was before the start # of the definition, start a new page, and replay the buffer _restart_definition() # Now do the operation we were asked to. # If the operation is PC relative, we need to pass it the current pc try: (arg, ) = args if callable(arg): args = (arg(asm.pc()), ) except ValueError: pass return function(*args, **kwargs)
def zero_equal(): """Logical not ( x -- flag ) flag is true if and only if x is equal to zero. """ label("forth.core.0=") adda(-add_cost_of_next(cost_of_zero_equal) / 2) # 1 ld(data_stack_page, Y) ld([data_stack_pointer], X) ld([Y, X]) st([Y, Xpp]) # 5 ora([Y, X]) beq(pc() + 3) # 7 bra(pc() + 3) # 8 ld(0x00) # 9 - If any bits were non-zero ld(0xFF) # 9 - Otherwise ld([data_stack_pointer], X) # 10 st([Y, Xpp]) st([Y, X]) # 12 NEXT(cost_of_zero_equal)
def exit(vTicks, vReturn): label("forth.exit") # Counting down label("forth.exit.from-failed-test") ld(-(cost_of_failed_next1 + 1) / 2) # 7 label("forth.exit.from-next1-reenter") label("forth.exit.from-next2") adda([vTicks]) # 6 ld(hi("vBlankStart"), Y) # 5 bgt(pc() & 0xFF) # 4 suba(1) # 3 jmp(Y, [vReturn]) # 2 nop() # 1
def emit_entry_page(vticks, vreturn): """Emit the data for NEXT and some other core routines The first page does not have the 'restart-or-quit' trampoline at 0x00 So we can't put any Forth word in here. """ while pc() & 255 < 255: nop() assert _next.INTERPRETER_ENTER_PAGE == pc() >> 8 label("FORTH_ENTER") C("You are now entering... Forth") adda(_next.INBOUND_TICK_CORRECTION) # --- Page boundary --- align(0x100, 0x100) st([vticks]) _next.next1(vticks) _next.next1_reenter(vticks) _next.next2(vticks) _next.exit(vticks, vreturn) _docol_exit.do_docol_rom() _docol_exit.do_docol_ram()
def move_ip(): """Page-Zero code to move the IP by the amount contained in AC This routine is used by the ROM mode next3, and also by literal, branch and zero_branch. As these routines all have different lengths, it uses a variable (tmp0) to tell it what length to return It always jumps to forth.next1.reenter.odd, and it has an odd length itself code calling it must have an even length """ assert pc() >> 8 == 0 label("forth.move-ip") adda([IP_lo]) # 1 st([IP_lo]) # 2 ld(hi("forth.next1.reenter"), Y) # 3 C("REENTER") jmp(Y, lo("forth.next1.reenter.odd")) # 4 ld([tmp0]) # 5
def next3_ram_ram(): label("forth.next3.ram-ram-mode") adda(-add_cost_of_reenter(cost_of_next3_ram_ram) // 2) # 1 # Copy low byte to zero-page ld([IP_hi], Y) C("[tmp] <- [IP]") ld([IP_lo], X) ld([Y, X]) st([Y, Xpp]) # 5; Advance X st([tmp0]) # High byte to zero-page ld([Y, X]) st([tmp1]) # Update W ld(AC, Y) C("[W] <- [tmp]") ld([tmp0], X) # 10 ld([Y, X]) st([W_lo]) st([Y, Xpp]) # Advance X ld([Y, X]) st([W_hi]) # 15 # Increment IP ld([IP_lo]) C("IP <- IP + 2") adda(2) bne(pc() + 8) # 18 st([IP_lo]) # 19 ld([IP_hi]) # 20 adda(1) st([IP_hi]) REENTER(cost_of_next3_ram_ram__page_crossed) label(".not-page-boundary") REENTER(cost_of_next3_ram_ram__no_page_cross)
def bitwise(): """Common implementation for all of the bitwise operators""" for name, target in [("AND", ".and"), ("OR", ".or"), ("XOR", ".xor")]: label(f"forth.core.{name}") adda(-add_cost_of_next(cost_of_binary_bitwise) / 2) # 1 bra(".copy-first-value") # 2 ld(lo(target)) # 3 label(".copy-first-value") st([tmp0]) # 4 adda(1) # 5 st([tmp1]) ld(data_stack_page, Y) ld([data_stack_pointer], X) ld(2) adda([data_stack_pointer]) # 10 st([data_stack_pointer]) # 11 for tmp in [tmp2, tmp3]: ld([Y, X]) st([tmp]) st([Y, Xpp]) # 17 = 11 + 2 * 3 ld([Y, X]) bra([tmp0]) bra(pc() + 1) # 20 # 21 st([Y, Xpp]) # 22 ld([Y, X]) bra([tmp1]) # 24 bra(".bitwise-done") # 25 # 26 for label_, op in [(".and", anda), (".or", ora), (".xor", xora)]: label(label_) op([tmp2]) op([tmp3]) label(".bitwise-done") st([Y, X]) # 27 NEXT(cost_of_binary_bitwise)
def _restart_definition(): """Restart the current definition on a fresh page Restores the asm module internal state to what it was before the start of the definition, start a new page, and replay the buffer. """ _restore_asm_state() _start_page() asm.label(_current_label) docol_rom_only() for function, args, kwargs in _assembler_call_buffer: # If the operation was PC relative, we need to pass it the current pc try: (arg, ) = args if callable(arg): args = (arg(asm.pc()), ) except ValueError: pass function(*args, **kwargs) _assembler_call_buffer[:] = [] # Clear # Restart capturing _capture_asm_state()
# First the high-bytes. # The table is shifted down by 32 places, as the first 32 high-bytes are all zero # This allows us to have code later in the page which we can branch back to. align(0x100, size=0x100) label("Quarter-squares lookup table") for i in range(32, 256): val = math.floor(i**2 / 4) ld(hi(val)) C(f"${val:04x} = {val} = floor({i} ** 2 / 4); ${val:04x} >> 8 = ${val >> 8:02x}" ) # We jump back here after looking up the low-byte of the result. label("low-byte return point") ld(hi("multiply 7x7"), Y) jmp(Y, [continuation]) ld(hi(pc()), Y) # Make it easy to get back here! cost_of_low_byte_return = 3 label("table entry.possibly-negative") # AC is negative, if b > a. Find absolute value blt(pc() + 3) # 1 bra(pc() + 3) # 2 suba(1) # 3; if >= 0 xora(0xFF) # 3; if < 0 adda(1) # 4 cost_of_absolute = 4 label("table entry") # Calculate an index into the high-byte table. # This is basically a matter of subtracting 32, and jumping in if the result >= 0. # But values greater than 160 have the sign-bit set after subtraction, # despite being >32. # We test for the sign bit and jump after subtraction even if 'negative' in these cases.