def value_point(ins): """ POINT: get pixel attribute at screen location. """ util.require_read(ins, ('(', )) lst = parse_expr_list(ins, 2, err=error.STX) util.require_read(ins, (')', )) if not lst[0]: raise error.RunError(error.STX) screen = state.console_state.screen if not lst[1]: # single-argument version try: x, y = screen.drawing.last_point fn = vartypes.pass_int_unpack(lst[0]) if fn == 0: return vartypes.pack_int(x) elif fn == 1: return vartypes.pack_int(y) elif fn == 2: fx, _ = screen.drawing.get_window_logical(x, y) return fp.pack(fx) elif fn == 3: _, fy = screen.drawing.get_window_logical(x, y) return fp.pack(fy) except AttributeError: return vartypes.null['%'] else: # two-argument mode if screen.mode.is_text_mode: raise error.RunError(error.IFC) return vartypes.pack_int( screen.drawing.point( (fp.unpack(vartypes.pass_single_keep(lst[0])), fp.unpack(vartypes.pass_single_keep(lst[1])), False)))
def value_point(ins): """ POINT: get pixel attribute at screen location. """ util.require_read(ins, ('(', )) arg0 = parse_expression(ins) screen = state.console_state.screen if util.skip_white_read_if(ins, (',', )): # two-argument mode arg1 = parse_expression(ins) util.require_read(ins, (')', )) if screen.mode.is_text_mode: raise error.RunError(error.IFC) return vartypes.int_to_integer_signed( screen.drawing.point( (fp.unpack(vartypes.pass_single(arg0)), fp.unpack(vartypes.pass_single(arg1)), False))) else: # single-argument mode util.require_read(ins, (')', )) try: x, y = screen.drawing.last_point fn = vartypes.pass_int_unpack(arg0) if fn == 0: return vartypes.int_to_integer_signed(x) elif fn == 1: return vartypes.int_to_integer_signed(y) elif fn == 2: fx, _ = screen.drawing.get_window_logical(x, y) return fp.pack(fx) elif fn == 3: _, fy = screen.drawing.get_window_logical(x, y) return fp.pack(fy) except AttributeError: return vartypes.null('%')
def value_pmap(ins): """ PMAP: convert between logical and physical coordinates. """ util.require_read(ins, ('(', )) coord = parse_expression(ins) util.require_read(ins, (',', )) mode = vartypes.pass_int_unpack(parse_expression(ins)) util.require_read(ins, (')', )) util.range_check(0, 3, mode) screen = state.console_state.screen if screen.mode.is_text_mode: return vartypes.null('%') if mode == 0: value, _ = screen.drawing.get_window_physical( fp.unpack(vartypes.pass_single(coord)), fp.Single.zero) return vartypes.int_to_integer_signed(value) elif mode == 1: _, value = screen.drawing.get_window_physical( fp.Single.zero, fp.unpack(vartypes.pass_single(coord))) return vartypes.int_to_integer_signed(value) elif mode == 2: value, _ = screen.drawing.get_window_logical( vartypes.pass_int_unpack(coord), 0) return fp.pack(value) elif mode == 3: _, value = screen.drawing.get_window_logical( 0, vartypes.pass_int_unpack(coord)) return fp.pack(value)
def value_point(ins): """ POINT: get pixel attribute at screen location. """ util.require_read(ins, ('(',)) lst = parse_expr_list(ins, 2, err=error.STX) util.require_read(ins, (')',)) if not lst[0]: raise error.RunError(error.STX) screen = state.console_state.screen if not lst[1]: # single-argument version try: x, y = screen.drawing.last_point fn = vartypes.pass_int_unpack(lst[0]) if fn == 0: return vartypes.pack_int(x) elif fn == 1: return vartypes.pack_int(y) elif fn == 2: fx, _ = screen.drawing.get_window_logical(x, y) return fp.pack(fx) elif fn == 3: _, fy = screen.drawing.get_window_logical(x, y) return fp.pack(fy) except AttributeError: return vartypes.null['%'] else: # two-argument mode if screen.mode.is_text_mode: raise error.RunError(error.IFC) return vartypes.pack_int(screen.drawing.point( (fp.unpack(vartypes.pass_single_keep(lst[0])), fp.unpack(vartypes.pass_single_keep(lst[1])), False)))
def value_point(ins): """ POINT: get pixel attribute at screen location. """ util.require_read(ins, ('(',)) arg0 = parse_expression(ins) screen = state.console_state.screen if util.skip_white_read_if(ins, (',',)): # two-argument mode arg1 = parse_expression(ins) util.require_read(ins, (')',)) if screen.mode.is_text_mode: raise error.RunError(error.IFC) return vartypes.int_to_integer_signed(screen.drawing.point( (fp.unpack(vartypes.pass_single(arg0)), fp.unpack(vartypes.pass_single(arg1)), False))) else: # single-argument mode util.require_read(ins, (')',)) try: x, y = screen.drawing.last_point fn = vartypes.pass_int_unpack(arg0) if fn == 0: return vartypes.int_to_integer_signed(x) elif fn == 1: return vartypes.int_to_integer_signed(y) elif fn == 2: fx, _ = screen.drawing.get_window_logical(x, y) return fp.pack(fx) elif fn == 3: _, fy = screen.drawing.get_window_logical(x, y) return fp.pack(fy) except AttributeError: return vartypes.null('%')
def value_operator(op, left, right): """ Get value of binary operator expression. """ if op == tk.O_CARET: return vcaret(left, right) elif op == tk.O_TIMES: return vtimes(left, right) elif op == tk.O_DIV: return vdiv(left, right) elif op == tk.O_INTDIV: return fp.pack( fp.div( fp.unpack(vartypes.pass_single_keep(left)).ifloor(), fp.unpack(vartypes.pass_single_keep( right)).ifloor()).apply_carry().ifloor()) elif op == tk.MOD: numerator = vartypes.pass_int_unpack(right) if numerator == 0: # simulate division by zero return fp.pack( fp.div( fp.unpack(vartypes.pass_single_keep(left)).ifloor(), fp.unpack( vartypes.pass_single_keep(right)).ifloor()).ifloor()) return vartypes.pack_int(vartypes.pass_int_unpack(left) % numerator) elif op == tk.O_PLUS: return vplus(left, right) elif op == tk.O_MINUS: return vartypes.number_add(left, vartypes.number_neg(right)) elif op == tk.O_GT: return vartypes.bool_to_int_keep(vartypes.gt(left, right)) elif op == tk.O_EQ: return vartypes.bool_to_int_keep(vartypes.equals(left, right)) elif op == tk.O_LT: return vartypes.bool_to_int_keep(not ( vartypes.gt(left, right) or vartypes.equals(left, right))) elif op == tk.O_GT + tk.O_EQ: return vartypes.bool_to_int_keep( vartypes.gt(left, right) or vartypes.equals(left, right)) elif op == tk.O_LT + tk.O_EQ: return vartypes.bool_to_int_keep(not vartypes.gt(left, right)) elif op == tk.O_LT + tk.O_GT: return vartypes.bool_to_int_keep(not vartypes.equals(left, right)) elif op == tk.AND: return vartypes.twoscomp_to_int( vartypes.pass_twoscomp(left) & vartypes.pass_twoscomp(right)) elif op == tk.OR: return vartypes.twoscomp_to_int( vartypes.pass_twoscomp(left) | vartypes.pass_twoscomp(right)) elif op == tk.XOR: return vartypes.twoscomp_to_int( vartypes.pass_twoscomp(left) ^ vartypes.pass_twoscomp(right)) elif op == tk.EQV: return vartypes.twoscomp_to_int(~(vartypes.pass_twoscomp(left) ^ vartypes.pass_twoscomp(right))) elif op == tk.IMP: return vartypes.twoscomp_to_int((~vartypes.pass_twoscomp(left)) | vartypes.pass_twoscomp(right)) else: raise error.RunError(error.STX)
def number_power(left, right): """ Left^right. """ if (left[0] == '#' or right[0] == '#') and vartypes.option_double: return fp.pack( fp.power(fp.unpack(vartypes.pass_double(left)), fp.unpack(vartypes.pass_double(right))) ) else: if right[0] == '%': return fp.pack( fp.unpack(vartypes.pass_single(left)).ipow_int(vartypes.integer_to_int_signed(right)) ) else: return fp.pack( fp.power(fp.unpack(vartypes.pass_single(left)), fp.unpack(vartypes.pass_single(right))) )
def number_add(left, right): """ Add two numbers. """ left, right = vartypes.pass_most_precise(left, right) if left[0] in ('#', '!'): return fp.pack(fp.unpack(left).iadd(fp.unpack(right))) else: # return Single to avoid wrapping on integer overflow return fp.pack(fp.Single.from_int(vartypes.integer_to_int_signed(left) + vartypes.integer_to_int_signed(right)))
def vcaret(left, right): """ Left^right. """ if (left[0] == '#' or right[0] == '#') and option_double: return fp.pack( fp.power(fp.unpack(vartypes.pass_double_keep(left)), fp.unpack(vartypes.pass_double_keep(right))) ) else: if right[0] == '%': return fp.pack( fp.unpack(vartypes.pass_single_keep(left)).ipow_int(vartypes.unpack_int(right)) ) else: return fp.pack( fp.power(fp.unpack(vartypes.pass_single_keep(left)), fp.unpack(vartypes.pass_single_keep(right))) )
def vtimes(left, right): """ Left*right. """ if left[0] == '#' or right[0] == '#': return fp.pack( fp.unpack(vartypes.pass_double_keep(left)).imul( fp.unpack(vartypes.pass_double_keep(right)))) else: return fp.pack( fp.unpack(vartypes.pass_single_keep(left)).imul( fp.unpack(vartypes.pass_single_keep(right))))
def vdiv(left, right): """ Left/right. """ if left[0] == '#' or right[0] == '#': return fp.pack( fp.div(fp.unpack(vartypes.pass_double_keep(left)), fp.unpack(vartypes.pass_double_keep(right)))) else: return fp.pack( fp.div(fp.unpack(vartypes.pass_single_keep(left)), fp.unpack(vartypes.pass_single_keep(right))))
def value_fix(ins): """ FIX: round towards zero. """ inp = vartypes.pass_number(parse_bracket(ins)) if inp[0] == '%': return inp elif inp[0] == '!': # needs to be a float to avoid overflow return fp.pack(fp.Single.from_int(fp.unpack(inp).trunc_to_int())) elif inp[0] == '#': return fp.pack(fp.Double.from_int(fp.unpack(inp).trunc_to_int()))
def pass_single_keep(num): """ Check if variable is numeric, convert to Single. """ if not num: raise error.RunError(error.STX) typechar = num[0] if typechar == '!': return num elif typechar == '%': return fp.pack(fp.Single.from_int(unpack_int(num))) elif typechar == '#': # *round* to single return fp.pack(fp.unpack(num).round_to_single()) elif typechar == '$': raise error.RunError(error.TYPE_MISMATCH)
def pass_single(num): """ Check if variable is numeric, convert to Single. """ if not num: raise error.RunError(error.STX) typechar = num[0] if typechar == '!': return num elif typechar == '%': return fp.pack(fp.Single.from_int(integer_to_int_signed(num))) elif typechar == '#': # *round* to single return fp.pack(fp.unpack(num).round_to_single()) elif typechar == '$': raise error.RunError(error.TYPE_MISMATCH)
def vcaret(left, right): """ Left^right. """ if (left[0] == '#' or right[0] == '#') and option_double: return fp.pack( fp.power(fp.unpack(vartypes.pass_double_keep(left)), fp.unpack(vartypes.pass_double_keep(right)))) else: if right[0] == '%': return fp.pack( fp.unpack(vartypes.pass_single_keep(left)).ipow_int( vartypes.unpack_int(right))) else: return fp.pack( fp.power(fp.unpack(vartypes.pass_single_keep(left)), fp.unpack(vartypes.pass_single_keep(right))))
def value_lof(ins): """ LOF: get length of file. """ util.skip_white(ins) num = vartypes.pass_int_unpack(parse_bracket(ins), maxint=0xffff) util.range_check(0, 255, num) the_file = devices.get_file(num) return fp.pack(fp.Single.from_int(the_file.lof()))
def number_add(left, right): """ Add two numbers. """ left, right = pass_most_precise_keep(left, right) if left[0] in ('#', '!'): return fp.pack(fp.unpack(left).iadd(fp.unpack(right))) else: return pack_int(unpack_int(left) + unpack_int(right))
def value_func(ins, fn): """ Return value of unary math function. """ return fp.pack( fn( fp.unpack( vartypes.pass_float(parse_bracket(ins), vartypes.option_double))))
def value_fre(ins): """ FRE: get free memory and optionally collect garbage. """ val = parse_bracket(ins) if val[0] == '$': # grabge collection if a string-valued argument is specified. var.collect_garbage() return fp.pack(fp.Single.from_int(var.fre()))
def value_operator(op, left, right): """ Get value of binary operator expression. """ if op == tk.O_CARET: return vcaret(left, right) elif op == tk.O_TIMES: return vtimes(left, right) elif op == tk.O_DIV: return vdiv(left, right) elif op == tk.O_INTDIV: return fp.pack(fp.div(fp.unpack(vartypes.pass_single_keep(left)).ifloor(), fp.unpack(vartypes.pass_single_keep(right)).ifloor()).apply_carry().ifloor()) elif op == tk.MOD: numerator = vartypes.pass_int_unpack(right) if numerator == 0: # simulate division by zero return fp.pack(fp.div(fp.unpack(vartypes.pass_single_keep(left)).ifloor(), fp.unpack(vartypes.pass_single_keep(right)).ifloor()).ifloor()) return vartypes.pack_int(vartypes.pass_int_unpack(left) % numerator) elif op == tk.O_PLUS: return vplus(left, right) elif op == tk.O_MINUS: return vartypes.number_add(left, vartypes.number_neg(right)) elif op == tk.O_GT: return vartypes.bool_to_int_keep(vartypes.gt(left,right)) elif op == tk.O_EQ: return vartypes.bool_to_int_keep(vartypes.equals(left, right)) elif op == tk.O_LT: return vartypes.bool_to_int_keep(not(vartypes.gt(left,right) or vartypes.equals(left, right))) elif op == tk.O_GT + tk.O_EQ: return vartypes.bool_to_int_keep(vartypes.gt(left,right) or vartypes.equals(left, right)) elif op == tk.O_LT + tk.O_EQ: return vartypes.bool_to_int_keep(not vartypes.gt(left,right)) elif op == tk.O_LT + tk.O_GT: return vartypes.bool_to_int_keep(not vartypes.equals(left, right)) elif op == tk.AND: return vartypes.twoscomp_to_int( vartypes.pass_twoscomp(left) & vartypes.pass_twoscomp(right) ) elif op == tk.OR: return vartypes.twoscomp_to_int( vartypes.pass_twoscomp(left) | vartypes.pass_twoscomp(right) ) elif op == tk.XOR: return vartypes.twoscomp_to_int( vartypes.pass_twoscomp(left) ^ vartypes.pass_twoscomp(right) ) elif op == tk.EQV: return vartypes.twoscomp_to_int( ~(vartypes.pass_twoscomp(left) ^ vartypes.pass_twoscomp(right)) ) elif op == tk.IMP: return vartypes.twoscomp_to_int( (~vartypes.pass_twoscomp(left)) | vartypes.pass_twoscomp(right) ) else: raise error.RunError(error.STX)
def value_erl(ins): """ ERL: get line number of last error. """ if state.basic_state.errp == 0: erl = 0 elif state.basic_state.errp == -1: erl = 65535 else: erl = program.get_line_number(state.basic_state.errp) return fp.pack(fp.Single.from_int(erl))
def get_random_int(n): """ Get a value from the random number generator (int argument). """ if n < 0: n = -n while n < 2**23: n *= 2 state.basic_state.rnd_seed = n if n != 0: state.basic_state.rnd_seed = (state.basic_state.rnd_seed*rnd_a + rnd_c) % rnd_period # rnd_seed/rnd_period return fp.pack(fp.div(fp.Single.from_int(state.basic_state.rnd_seed), fp.Single.from_int(rnd_period)))
def _handle_math_error(e): """ Handle Overflow or Division by Zero. """ if isinstance(e, ValueError): # math domain errors such as SQR(-1) raise error.RunError(error.IFC) elif isinstance(e, OverflowError): math_error = error.OVERFLOW elif isinstance(e, ZeroDivisionError): math_error = error.DIVISION_BY_ZERO else: raise e if state.basic_state.on_error: # also raises exception in error_handle_mode! # in that case, prints a normal error message raise error.RunError(math_error) else: # write a message & continue as normal console.write_line(error.RunError(math_error).message) # return max value for the appropriate float type if e.args and e.args[0] and isinstance(e.args[0], fp.Float): return fp.pack(e.args[0]) return fp.pack(fp.Single.max.copy())
def pass_double(num): """ Check if variable is numeric, convert to Double. """ if not num: raise error.RunError(error.STX) typechar = num[0] if typechar == '#': return num elif typechar == '%': return fp.pack(fp.Double.from_int(integer_to_int_signed(num))) elif typechar == '!': return ('#', bytearray(4) + num[1]) elif typechar == '$': raise error.RunError(error.TYPE_MISMATCH)
def pass_double_keep(num): """ Check if variable is numeric, convert to Double. """ if not num: raise error.RunError(error.STX) typechar = num[0] if typechar == '#': return num elif typechar == '%': return fp.pack(fp.Double.from_int(unpack_int(num))) elif typechar == '!': return ('#', bytearray(4) + num[1]) elif typechar == '$': raise error.RunError(error.TYPE_MISMATCH)
def number_abs(inp): """ Return the absolute value of a number. """ if inp[0] == '%': val = abs(unpack_int(inp)) if val == 32768: return fp.pack(fp.Single.from_int(val)) else: return pack_int(val) elif inp[0] in ('!', '#'): out = (inp[0], inp[1][:]) out[1][-2] &= 0x7F return out return inp
def value_pmap(ins): """ PMAP: convert between logical and physical coordinates. """ util.require_read(ins, ('(',)) coord = parse_expression(ins) util.require_read(ins, (',',)) mode = vartypes.pass_int_unpack(parse_expression(ins)) util.require_read(ins, (')',)) util.range_check(0, 3, mode) screen = state.console_state.screen if screen.mode.is_text_mode: return vartypes.null('%') if mode == 0: value, _ = screen.drawing.get_window_physical(fp.unpack(vartypes.pass_single(coord)), fp.Single.zero) return vartypes.int_to_integer_signed(value) elif mode == 1: _, value = screen.drawing.get_window_physical(fp.Single.zero, fp.unpack(vartypes.pass_single(coord))) return vartypes.int_to_integer_signed(value) elif mode == 2: value, _ = screen.drawing.get_window_logical(vartypes.pass_int_unpack(coord), 0) return fp.pack(value) elif mode == 3: _, value = screen.drawing.get_window_logical(0, vartypes.pass_int_unpack(coord)) return fp.pack(value)
def get_random_int(n): """ Get a value from the random number generator (int argument). """ if n < 0: n = -n while n < 2**23: n *= 2 state.basic_state.rnd_seed = n if n != 0: state.basic_state.rnd_seed = (state.basic_state.rnd_seed * rnd_a + rnd_c) % rnd_period # rnd_seed/rnd_period return fp.pack( fp.div(fp.Single.from_int(state.basic_state.rnd_seed), fp.Single.from_int(rnd_period)))
def number_neg(inp): """ Return the negation of a number. """ if inp[0] == '%': val = -unpack_int(inp) if val == 32768: return fp.pack(fp.Single.from_int(val)) else: return pack_int(val) elif inp[0] in ('!', '#'): out = (inp[0], inp[1][:]) out[1][-2] ^= 0x80 return out # pass strings on, let error happen somewhere else. return inp
def number_neg(inp): """ Return the negation of a number. """ inp = vartypes.pass_number(inp) if inp[0] == '%': val = -vartypes.integer_to_int_signed(inp) if val == 32768: return fp.pack(fp.Single.from_int(val)) else: return vartypes.int_to_integer_signed(val) elif inp[0] in ('!', '#'): out = (inp[0], inp[1][:]) out[1][-2] ^= 0x80 return out # pass strings on, let error happen somewhere else. return inp
def number_multiply(left, right): """ Left*right. """ if left[0] == '#' or right[0] == '#': return fp.pack( fp.unpack(vartypes.pass_double(left)).imul(fp.unpack(vartypes.pass_double(right))) ) else: return fp.pack( fp.unpack(vartypes.pass_single(left)).imul(fp.unpack(vartypes.pass_single(right))) )
def value_int(ins): """ INT: get floor value. """ inp = vartypes.pass_number(parse_bracket(ins)) return inp if inp[0] == '%' else fp.pack(fp.unpack(inp).ifloor())
def value_func(ins, fn): """ Return value of unary math function. """ return fp.pack(fn(fp.unpack(vartypes.pass_float(parse_bracket(ins), vartypes.option_double))))
def number_divide(left, right): """ Left/right. """ if left[0] == '#' or right[0] == '#': return fp.pack( fp.div(fp.unpack(vartypes.pass_double(left)), fp.unpack(vartypes.pass_double(right))) ) else: return fp.pack( fp.div(fp.unpack(vartypes.pass_single(left)), fp.unpack(vartypes.pass_single(right))) )
def vdiv(left, right): """ Left/right. """ if left[0] == '#' or right[0] == '#': return fp.pack( fp.div(fp.unpack(vartypes.pass_double_keep(left)), fp.unpack(vartypes.pass_double_keep(right))) ) else: return fp.pack( fp.div(fp.unpack(vartypes.pass_single_keep(left)), fp.unpack(vartypes.pass_single_keep(right))) )
def value_timer(ins): """ TIMER: get clock ticks since midnight. """ # precision of GWBASIC TIMER is about 1/20 of a second return fp.pack(fp.div( fp.Single.from_int(timedate.timer_milliseconds()/50), fp.Single.from_int(20)))
def vtimes(left, right): """ Left*right. """ if left[0] == '#' or right[0] == '#': return fp.pack( fp.unpack(vartypes.pass_double_keep(left)).imul(fp.unpack(vartypes.pass_double_keep(right))) ) else: return fp.pack( fp.unpack(vartypes.pass_single_keep(left)).imul(fp.unpack(vartypes.pass_single_keep(right))) )
def value_erl(ins): """ ERL: get line number of last error. """ return fp.pack( fp.Single.from_int(program.get_line_number(state.basic_state.errp)))
def value_timer(ins): """ TIMER: get clock ticks since midnight. """ # precision of GWBASIC TIMER is about 1/20 of a second return fp.pack( fp.div(fp.Single.from_int(timedate.timer_milliseconds() / 50), fp.Single.from_int(20)))
def value_erl(ins): """ ERL: get line number of last error. """ return fp.pack(fp.Single.from_int(program.get_line_number(state.basic_state.errp)))