def renum(new_line, start_line, step): """ Renumber stored program. """ new_line = 10 if new_line is None else new_line start_line = 0 if start_line is None else start_line step = 10 if step is None else step # get a sorted list of line numbers keys = sorted([ k for k in state.basic_state.line_numbers.keys() if k >= start_line]) # assign the new numbers old_to_new = {} for old_line in keys: if old_line < 65535 and new_line > 65529: raise error.RunError(error.IFC) if old_line == 65536: break old_to_new[old_line] = new_line state.basic_state.last_stored = new_line new_line += step # write the new numbers for old_line in old_to_new: state.basic_state.bytecode.seek(state.basic_state.line_numbers[old_line]) # skip the \x00\xC0\xDE & overwrite line number state.basic_state.bytecode.read(3) state.basic_state.bytecode.write(str(vartypes.value_to_uint(old_to_new[old_line]))) # rebuild the line number dictionary new_lines = {} for old_line in old_to_new: new_lines[old_to_new[old_line]] = state.basic_state.line_numbers[old_line] del state.basic_state.line_numbers[old_line] state.basic_state.line_numbers.update(new_lines) # write the indirect line numbers state.basic_state.bytecode.seek(0) while util.skip_to_read(state.basic_state.bytecode, ('\x0e',)) == '\x0e': # get the old g number jumpnum = vartypes.uint_to_value(bytearray(state.basic_state.bytecode.read(2))) try: newjump = old_to_new[jumpnum] except KeyError: # not redefined, exists in program? if jumpnum in state.basic_state.line_numbers: newjump = jumpnum else: linum = get_line_number(state.basic_state.bytecode.tell()) console.write_line('Undefined line ' + str(jumpnum) + ' in ' + str(linum)) state.basic_state.bytecode.seek(-2, 1) state.basic_state.bytecode.write(str(vartypes.value_to_uint(newjump))) # stop running if we were flow.set_pointer(False) # reset loop stacks state.basic_state.gosub_return = [] state.basic_state.for_next_stack = [] state.basic_state.while_wend_stack = [] # renumber error handler if state.basic_state.on_error: state.basic_state.on_error = old_to_new[state.basic_state.on_error] # renumber event traps for handler in state.basic_state.events.all: if handler.gosub: handler.set_jump(old_to_new[handler.gosub])
def bsave(g, offset, length): """ Save a block of memory into a file. """ addr = state.basic_state.segment * 0x10 + offset g.write(str(get_memory_block(addr, length))) # Tandys repeat the header at the end of the file if tandy_syntax: g.write('\xfd' + vartypes.value_to_uint(state.basic_state.segment) + vartypes.value_to_uint(offset) + vartypes.value_to_uint(length))
def rebuild_line_dict(): """ Preparse to build line number dictionary. """ state.basic_state.line_numbers, offsets = {}, [] state.basic_state.bytecode.seek(0) scanline, scanpos, last = 0, 0, 0 while True: state.basic_state.bytecode.read(1) # pass \x00 scanline = util.parse_line_number(state.basic_state.bytecode) if scanline == -1: scanline = 65536 # if parse_line_number returns -1, it leaves the stream pointer here: 00 _00_ 00 1A break state.basic_state.line_numbers[scanline] = scanpos last = scanpos util.skip_to(state.basic_state.bytecode, tk.end_line) scanpos = state.basic_state.bytecode.tell() offsets.append(scanpos) state.basic_state.line_numbers[65536] = scanpos # rebuild offsets state.basic_state.bytecode.seek(0) last = 0 for pos in offsets: state.basic_state.bytecode.read(1) state.basic_state.bytecode.write(str(vartypes.value_to_uint(program_memory_start + pos))) state.basic_state.bytecode.read(pos - last - 3) last = pos # ensure program is properly sealed - last offset must be 00 00. keep, but ignore, anything after. state.basic_state.bytecode.write('\0\0\0')
def get_data_memory_array(address): """ Retrieve data from data memory: array space """ name_addr = -1 arr_addr = -1 the_arr = None for name in state.basic_state.array_memory: name_try, arr_try = state.basic_state.array_memory[name] if name_try <= address and name_try > name_addr: name_addr, arr_addr = name_try, arr_try the_arr = name if the_arr is None: return -1 if address >= state.basic_state.var_current + arr_addr: offset = address - arr_addr - state.basic_state.var_current if offset >= var.array_size_bytes(the_arr): return -1 _, byte_array, _ = state.basic_state.arrays[the_arr] return byte_array[offset] else: offset = address - name_addr - state.basic_state.var_current if offset < max(3, len(the_arr)) + 1: return get_name_in_memory(the_arr, offset) else: offset -= max(3, len(the_arr)) + 1 dimensions, _, _ = state.basic_state.arrays[the_arr] data_rep = vartypes.value_to_uint( var.array_size_bytes(the_arr) + 1 + 2 * len(dimensions)) + chr( len(dimensions)) for d in dimensions: data_rep += vartypes.value_to_uint( d + 1 - state.basic_state.array_base) return data_rep[offset]
def get_data_memory_array(address): """ Retrieve data from data memory: array space """ name_addr = -1 arr_addr = -1 the_arr = None for name in state.basic_state.array_memory: name_try, arr_try = state.basic_state.array_memory[name] if name_try <= address and name_try > name_addr: name_addr, arr_addr = name_try, arr_try the_arr = name if the_arr == None: return -1 if address >= state.basic_state.var_current + arr_addr: offset = address - arr_addr - state.basic_state.var_current if offset >= var.array_size_bytes(the_arr): return -1 _, byte_array, _ = state.basic_state.arrays[the_arr] return byte_array[offset] else: offset = address - name_addr - state.basic_state.var_current if offset < max(3, len(the_arr))+1: return get_name_in_memory(the_arr, offset) else: offset -= max(3, len(the_arr))+1 dimensions, _, _ = state.basic_state.arrays[the_arr] data_rep = vartypes.value_to_uint(var.array_size_bytes(the_arr) + 1 + 2*len(dimensions)) + chr(len(dimensions)) for d in dimensions: data_rep += vartypes.value_to_uint(d + 1 - state.basic_state.array_base) return data_rep[offset]
def set_field_var_or_array(random_file, varname, indices, offset, length): """ Attach a string variable to a FIELD buffer. """ if varname[-1] != '$': # type mismatch raise error.RunError(error.TYPE_MISMATCH) if offset + length > len(random_file.field.buffer): # FIELD overflow raise error.RunError(error.FIELD_OVERFLOW) str_addr = random_file.field.address + offset str_sequence = bytearray(chr(length)) + vartypes.value_to_uint(str_addr) # assign the string ptr to the variable name # desired side effect: if we re-assign this string variable through LET, it's no longer connected to the FIELD. if indices == []: state.basic_state.variables[varname] = str_sequence # update memory model (see set_var) if varname not in state.basic_state.var_memory: name_ptr = state.basic_state.var_current var_ptr = name_ptr + max( 3, len(varname) ) + 1 # byte_size first_letter second_letter_or_nul remaining_length_or_nul state.basic_state.var_current += max( 3, len(varname)) + 1 + byte_size['$'] state.basic_state.var_memory[varname] = (name_ptr, var_ptr) else: check_dim_array(varname, indices) dimensions, lst, _ = state.basic_state.arrays[varname] bigindex = index_array(indices, dimensions) lst[bigindex * 3:(bigindex + 1) * 3] = str_sequence
def store_line(linebuf): """ Store the given line buffer. """ if state.basic_state.protected: raise error.RunError(error.IFC) # get the new line number linebuf.seek(1) scanline = util.parse_line_number(linebuf) # check if linebuf is an empty line after the line number empty = (util.skip_white_read(linebuf) in tk.end_line) pos, afterpos, deleteable, beyond = find_pos_line_dict(scanline, scanline) if empty and not deleteable: raise error.RunError(error.UNDEFINED_LINE_NUMBER) # read the remainder of the program into a buffer to be pasted back after the write state.basic_state.bytecode.seek(afterpos) rest = state.basic_state.bytecode.read() # insert state.basic_state.bytecode.seek(pos) # write the line buffer to the program buffer length = 0 if not empty: # set offsets linebuf.seek(3) # pass \x00\xC0\xDE length = len(linebuf.getvalue()) state.basic_state.bytecode.write( '\0' + str(vartypes.value_to_uint(program_memory_start + pos + length)) + linebuf.read()) # write back the remainder of the program truncate_program(rest) # update all next offsets by shifting them by the length of the added line update_line_dict(pos, afterpos, length, deleteable, beyond) if not empty: state.basic_state.line_numbers[scanline] = pos # clear all program stacks flow.init_program() state.basic_state.last_stored = scanline
def tokenise_uint(ins): """ Convert an unsigned int (line number) to tokenised form. """ word = bytearray() while True: c = ins.read(1) if c in ascii_digits + whitespace: word += c else: ins.seek(-len(c), 1) break # don't claim trailing w/s while len(word)>0 and chr(word[-1]) in whitespace: del word[-1] ins.seek(-1, 1) # remove all whitespace trimword = bytearray() for c in word: if chr(c) not in whitespace: trimword += chr(c) word = trimword # line number (jump) if len(word) > 0: if int(word) >= 65530: # note: anything >= 65530 is illegal in GW-BASIC # in loading an ASCII file, GWBASIC would interpret these as # '6553 1' etcetera, generating a syntax error on load. # keep 6553 as line number and push back the last number: ins.seek(4-len(word), 1) word = word[:4] return str(vartypes.value_to_uint(int(word))) else: return ''
def set_field_var_or_array(random_file, varname, indices, offset, length): """ Attach a string variable to a FIELD buffer. """ if varname[-1] != '$': # type mismatch raise error.RunError(13) field = random_file.field if offset+length > len(field): # FIELD overflow raise error.RunError(50) str_addr = random_file.field_address + offset str_sequence = bytearray(chr(length)) + vartypes.value_to_uint(str_addr) # assign the string ptr to the variable name # desired side effect: if we re-assign this string variable through LET, it's no longer connected to the FIELD. if indices == []: state.basic_state.variables[varname] = str_sequence # update memory model (see set_var) if varname not in state.basic_state.var_memory: name_ptr = state.basic_state.var_current var_ptr = name_ptr + max(3, len(varname)) + 1 # byte_size first_letter second_letter_or_nul remaining_length_or_nul state.basic_state.var_current += max(3, len(varname)) + 1 + byte_size['$'] state.basic_state.var_memory[varname] = (name_ptr, var_ptr) else: check_dim_array(varname, indices) dimensions, lst, _ = state.basic_state.arrays[varname] bigindex = index_array(indices, dimensions) lst[bigindex*3:(bigindex+1)*3] = str_sequence
def tokenise_uint(ins): """ Convert an unsigned int (line number) to tokenised form. """ word = bytearray() while True: c = ins.read(1) if c and c in ascii_digits + ascii_whitespace: word += c else: ins.seek(-len(c), 1) break # don't claim trailing w/s while len(word) > 0 and chr(word[-1]) in ascii_whitespace: del word[-1] ins.seek(-1, 1) # remove all whitespace trimword = bytearray() for c in word: if chr(c) not in ascii_whitespace: trimword += chr(c) word = trimword # line number (jump) if len(word) > 0: if int(word) >= 65530: # note: anything >= 65530 is illegal in GW-BASIC # in loading an ASCII file, GWBASIC would interpret these as # '6553 1' etcetera, generating a syntax error on load. # keep 6553 as line number and push back the last number: ins.seek(4 - len(word), 1) word = word[:4] return str(vartypes.value_to_uint(int(word))) else: return ''
def rebuild_line_dict(): """ Preparse to build line number dictionary. """ state.basic_state.line_numbers, offsets = {}, [] state.basic_state.bytecode.seek(0) scanline, scanpos, last = 0, 0, 0 while True: state.basic_state.bytecode.read(1) # pass \x00 scanline = util.parse_line_number(state.basic_state.bytecode) if scanline == -1: scanline = 65536 # if parse_line_number returns -1, it leaves the stream pointer here: 00 _00_ 00 1A break state.basic_state.line_numbers[scanline] = scanpos last = scanpos util.skip_to(state.basic_state.bytecode, tk.end_line) scanpos = state.basic_state.bytecode.tell() offsets.append(scanpos) state.basic_state.line_numbers[65536] = scanpos # rebuild offsets state.basic_state.bytecode.seek(0) last = 0 for pos in offsets: state.basic_state.bytecode.read(1) state.basic_state.bytecode.write( str(vartypes.value_to_uint(program_memory_start + pos))) state.basic_state.bytecode.read(pos - last - 3) last = pos # ensure program is properly sealed - last offset must be 00 00. keep, but ignore, anything after. state.basic_state.bytecode.write('\0\0\0')
def tokenise_hex(ins, outs): """ Convert hex expression in Python string to number token. """ ins.read(1) word = '' while True: c = util.peek(ins) if not c or c not in string.hexdigits: break else: word += ins.read(1) val = int(word, 16) if word else 0 outs.write(tk.T_HEX + str(vartypes.value_to_uint(val)))
def tokenise_hex(ins, outs): """ Convert hex expression in Python string to number token. """ ins.read(1) word = '' while True: c = util.peek(ins).upper() if not c or c not in ascii_hexits: break else: word += ins.read(1).upper() val = int(word, 16) if word else 0 outs.write(tk.T_HEX + str(vartypes.value_to_uint(val)))
def store(self, string_buffer, address=None): """ Store a new string and return the 3-byte memory sequence. """ if address == None: # find new string address self.current -= len(string_buffer) address = self.current + 1 key = str(vartypes.value_to_uint(address)) # don't store empty strings if len(string_buffer) > 0: if key in self.strings: raise KeyError('String key %s at %d already defined.' % (repr(key), address)) self.strings[key] = string_buffer return bytearray(chr(len(string_buffer)) + key)
def tokenise_oct(ins, outs): """ Convert octal expression in Python string to number token. """ # O is optional, could also be &777 instead of &O777 if util.peek(ins).upper() == 'O': ins.read(1) word = '' while True: c = util.peek(ins).upper() if not c or c not in ascii_octits: break else: word += ins.read(1).upper() val = int(word, 8) if word else 0 outs.write(tk.T_OCT + str(vartypes.value_to_uint(val)))
def store(self, string_buffer, address=None): """ Store a new string and return the 3-byte memory sequence. """ if address is None: # find new string address self.current -= len(string_buffer) address = self.current + 1 key = str(vartypes.value_to_uint(address)) # don't store empty strings if len(string_buffer) > 0: if key in self.strings: raise KeyError('String key %s at %d already defined.' % (repr(key), address)) self.strings[key] = string_buffer return bytearray(chr(len(string_buffer)) + key)
def tokenise_oct(ins, outs): """ Convert octal expression in Python string to number token. """ # O is optional, could also be &777 instead of &O777 if util.peek(ins).upper() == 'O': ins.read(1) word = '' while True: c = util.peek(ins) if not c or c not in string.octdigits: break else: word += ins.read(1) val = int(word, 8) if word else 0 outs.write(tk.T_OCT + str(vartypes.value_to_uint(val)))
def __init__(self, fhandle, filetype, number, name, mode, seg, offset, length): """ Initialise program file object and write header. """ iolayer.RawFile.__init__(self, fhandle, filetype, mode) self.number = number # don't lock binary files self.lock = '' self.access = 'RW' self.seg, self.offset, self.length = 0, 0, 0 if self.mode == 'O': self.write(iolayer.type_to_magic[filetype]) if self.filetype == 'M': self.write(vartypes.value_to_uint(seg) + vartypes.value_to_uint(offset) + vartypes.value_to_uint(length)) self.seg, self.offset, self.length = seg, offset, length else: # drop magic byte self.read_raw(1) if self.filetype == 'M': self.seg = vartypes.uint_to_value(bytearray(self.read(2))) self.offset = vartypes.uint_to_value(bytearray(self.read(2))) # size gets ignored: even the \x1a at the end is read self.length = vartypes.uint_to_value(bytearray(self.read(2)))
def __init__(self, fhandle, filetype, number, name, mode, seg, offset, length): """ Initialise program file object and write header. """ devices.RawFile.__init__(self, fhandle, filetype, mode) self.number = number # don't lock binary files self.lock = '' self.access = 'RW' self.seg, self.offset, self.length = 0, 0, 0 if self.mode == 'O': self.write(devices.type_to_magic[filetype]) if self.filetype == 'M': self.write(vartypes.value_to_uint(seg) + vartypes.value_to_uint(offset) + vartypes.value_to_uint(length)) self.seg, self.offset, self.length = seg, offset, length else: # drop magic byte self.read_raw(1) if self.filetype == 'M': self.seg = vartypes.uint_to_value(bytearray(self.read(2))) self.offset = vartypes.uint_to_value(bytearray(self.read(2))) # size gets ignored: even the \x1a at the end is read self.length = vartypes.uint_to_value(bytearray(self.read(2)))
def value_varptr(ins): """ VARPTR, VARPTR$: get memory address for variable or FCB. """ dollar = util.skip_white_read_if(ins, ('$',)) util.require_read(ins, ('(',)) if (not dollar) and util.skip_white(ins) == '#': filenum = parse_file_number_opthash(ins) var_ptr = machine.varptr_file(filenum) else: name, indices = get_var_or_array_name(ins) var_ptr = machine.varptr(name, indices) util.require_read(ins, (')',)) if var_ptr < 0: raise error.RunError(error.IFC) if dollar: return vartypes.pack_string(bytearray(chr(var.byte_size[name[-1]])) + vartypes.value_to_uint(var_ptr)) else: return vartypes.pack_int(var_ptr)
def value_varptr(ins): """ VARPTR, VARPTR$: get memory address for variable or FCB. """ dollar = util.skip_white_read_if(ins, ('$', )) util.require_read(ins, ('(', )) if (not dollar) and util.skip_white(ins) == '#': filenum = parse_file_number_opthash(ins) var_ptr = machine.varptr_file(filenum) else: name, indices = get_var_or_array_name(ins) var_ptr = machine.varptr(name, indices) util.require_read(ins, (')', )) if var_ptr < 0: raise error.RunError(error.IFC) if dollar: return vartypes.pack_string( bytearray(chr(var.byte_size[name[-1]])) + vartypes.value_to_uint(var_ptr)) else: return vartypes.pack_int(var_ptr)
def update_line_dict(pos, afterpos, length, deleteable, beyond): """ Update line number dictionary after deleting lines. """ # subtract length of line we replaced length -= afterpos - pos addr = program_memory_start + afterpos state.basic_state.bytecode.seek(afterpos + length + 1) # pass \x00 while True: next_addr = bytearray(state.basic_state.bytecode.read(2)) if len(next_addr) < 2 or next_addr == '\0\0': break next_addr = vartypes.uint_to_value(next_addr) state.basic_state.bytecode.seek(-2, 1) state.basic_state.bytecode.write(str(vartypes.value_to_uint(next_addr + length))) state.basic_state.bytecode.read(next_addr - addr - 2) addr = next_addr # update line number dict for key in deleteable: del state.basic_state.line_numbers[key] for key in beyond: state.basic_state.line_numbers[key] += length
def update_line_dict(pos, afterpos, length, deleteable, beyond): """ Update line number dictionary after deleting lines. """ # subtract length of line we replaced length -= afterpos - pos addr = program_memory_start + afterpos state.basic_state.bytecode.seek(afterpos + length + 1) # pass \x00 while True: next_addr = bytearray(state.basic_state.bytecode.read(2)) if len(next_addr) < 2 or next_addr == '\0\0': break next_addr = vartypes.uint_to_value(next_addr) state.basic_state.bytecode.seek(-2, 1) state.basic_state.bytecode.write( str(vartypes.value_to_uint(next_addr + length))) state.basic_state.bytecode.read(next_addr - addr - 2) addr = next_addr # update line number dict for key in deleteable: del state.basic_state.line_numbers[key] for key in beyond: state.basic_state.line_numbers[key] += length
def sprite_size_to_record(self, dx, dy): """ Write 4-byte record of sprite size. """ return vartypes.value_to_uint( dx * self.bitsperpixel) + vartypes.value_to_uint(dy)
def tokenise_number(ins, outs): """ Convert Python-string number representation to number token. """ c = util.peek(ins) # handle hex or oct constants if c == '&': ins.read(1) nxt = util.peek(ins).upper() if nxt == 'H': # hex constant ins.read(1) word = '' while True: if not util.peek(ins).upper() in ascii_hexits: break else: word += ins.read(1).upper() val = int(word, 16) if word else 0 outs.write('\x0C' + str(vartypes.value_to_uint(val))) else: # nxt == 'O': # octal constant if nxt == 'O': ins.read(1) word = '' while True: if not util.peek(ins).upper() in ascii_octits: break else: word += ins.read(1).upper() val = int(word, 8) if word else 0 outs.write('\x0B' + str(vartypes.value_to_uint(val))) # handle other numbers # note GW passes signs separately as a token and only stores positive numbers in the program elif (c in ascii_digits or c=='.' or c in ('+','-')): have_exp = False have_point = False word = '' while True: c = ins.read(1).upper() if c == '.' and not have_point and not have_exp: have_point = True word += c elif c in ('E', 'D') and not have_exp: have_exp = True word += c elif c in ('-','+') and word=='': # must be first token word += c elif c in ('+', '-') and word[-1] in ('E', 'D'): word += c elif c in ascii_digits: # (c >='0' and numc <='9'): word += c elif c in whitespace: # we'll remove this later but need to keep it for now so we can reposition the stream on removing trainling whitespace word += c elif c in ('!', '#') and not have_exp: word += c break elif c == '%': # swallow a %, but break parsing break else: if c != '': ins.seek(-1,1) break # don't claim trailing whitespace, don't end in D or E while len(word)>0 and (word[-1] in whitespace + ('D', 'E')): if word[-1] in ('D', 'E'): have_exp = False word = word[:-1] ins.seek(-1,1) # even if c=='' # remove all internal whitespace trimword = '' for c in word: if c not in whitespace: trimword += c word = trimword # write out the numbers if len(word) == 1 and word in ascii_digits: # digit outs.write(chr(0x11+int(word))) elif not (have_exp or have_point or word[-1] in ('!', '#')) and int(word) <= 0x7fff and int(word) >= -0x8000: if int(word) <= 0xff and int(word)>=0: # one-byte constant outs.write('\x0f'+chr(int(word))) else: # two-byte constant outs.write('\x1c'+str(vartypes.value_to_sint(int(word)))) else: mbf = str(from_str(word).to_bytes()) if len(mbf) == 4: # single outs.write('\x1d'+mbf) else: # double outs.write('\x1f'+mbf) elif c!='': ins.seek(-1,1)
def renum(new_line, start_line, step): """ Renumber stored program. """ new_line = 10 if new_line is None else new_line start_line = 0 if start_line is None else start_line step = 10 if step is None else step # get a sorted list of line numbers keys = sorted( [k for k in state.basic_state.line_numbers.keys() if k >= start_line]) # assign the new numbers old_to_new = {} for old_line in keys: if old_line < 65535 and new_line > 65529: raise error.RunError(error.IFC) if old_line == 65536: break old_to_new[old_line] = new_line state.basic_state.last_stored = new_line new_line += step # write the new numbers for old_line in old_to_new: state.basic_state.bytecode.seek( state.basic_state.line_numbers[old_line]) # skip the \x00\xC0\xDE & overwrite line number state.basic_state.bytecode.read(3) state.basic_state.bytecode.write( str(vartypes.value_to_uint(old_to_new[old_line]))) # rebuild the line number dictionary new_lines = {} for old_line in old_to_new: new_lines[ old_to_new[old_line]] = state.basic_state.line_numbers[old_line] del state.basic_state.line_numbers[old_line] state.basic_state.line_numbers.update(new_lines) # write the indirect line numbers state.basic_state.bytecode.seek(0) while util.skip_to_read(state.basic_state.bytecode, ('\x0e', )) == '\x0e': # get the old g number jumpnum = vartypes.uint_to_value( bytearray(state.basic_state.bytecode.read(2))) try: newjump = old_to_new[jumpnum] except KeyError: # not redefined, exists in program? if jumpnum in state.basic_state.line_numbers: newjump = jumpnum else: linum = get_line_number(state.basic_state.bytecode.tell()) console.write_line('Undefined line ' + str(jumpnum) + ' in ' + str(linum)) state.basic_state.bytecode.seek(-2, 1) state.basic_state.bytecode.write(str(vartypes.value_to_uint(newjump))) # stop running if we were flow.set_pointer(False) # reset loop stacks state.basic_state.gosub_return = [] state.basic_state.for_next_stack = [] state.basic_state.while_wend_stack = [] # renumber error handler if state.basic_state.on_error: state.basic_state.on_error = old_to_new[state.basic_state.on_error] # renumber event traps for handler in state.basic_state.events.all: if handler.gosub: handler.set_jump(old_to_new[handler.gosub])
def sprite_size_to_record(self, dx, dy): """ Write 4-byte record of sprite size. """ return vartypes.value_to_uint(dx*self.bitsperpixel) + vartypes.value_to_uint(dy)
def sprite_size_to_record_ega(self, dx, dy): """ Write 4-byte record of sprite size in EGA modes. """ return vartypes.value_to_uint(dx) + vartypes.value_to_uint(dy)