[pypy-svn] r62437 - in pypy/trunk/pypy/lang/gameboy: . test

tverwaes at codespeak.net tverwaes at codespeak.net
Tue Mar 3 01:58:53 CET 2009


Author: tverwaes
Date: Tue Mar  3 01:58:50 2009
New Revision: 62437

Modified:
   pypy/trunk/pypy/lang/gameboy/constants.py
   pypy/trunk/pypy/lang/gameboy/test/test_video.py
   pypy/trunk/pypy/lang/gameboy/test/test_video_registers.py
   pypy/trunk/pypy/lang/gameboy/video.py
   pypy/trunk/pypy/lang/gameboy/video_register.py
   pypy/trunk/pypy/lang/gameboy/video_sprite.py
Log:
major refactoring. moving from hardcoded tile addresses and vram to tile_maps
containing tile_groups containing indexes in a tile_data array containing
tiles which know how to draw themselves.

Also corrected the sizes in the constants file. 8 - 2 = 6; not 8 :)


Modified: pypy/trunk/pypy/lang/gameboy/constants.py
==============================================================================
--- pypy/trunk/pypy/lang/gameboy/constants.py	(original)
+++ pypy/trunk/pypy/lang/gameboy/constants.py	Tue Mar  3 01:58:50 2009
@@ -125,8 +125,16 @@
 VRAM_SIZE   = 0x2000
 
 # VRAM Tile Data/Maps Addresses
-VRAM_DATA_A = 0x0000 # 4KB Tile Data (8000..8FFF)
-VRAM_DATA_B = 0x0800 # 4KB Tile Data (8800..97FF)
+TILE_DATA_SIZE = 0x00C0 # 1/4th of 0x2000; 2 data maps with 2 bytes per tile
+                        # line and 8 lines
+TILE_DATA_ADDR = 0x8000
+
+VRAM_DATA_A = 0x0000 # 3KB Tile Data (8000..8BFF)
+VRAM_DATA_B = 0x0C00 # 3KB Tile Data (8C00..97FF)
+
+TILE_MAP_ADDR   = 0x9800
+TILE_MAP_SIZE   = 32 # 32 Groups
+TILE_GROUP_SIZE = 32 # of 32 addresses
 
 VRAM_MAP_A  = 0x1800 # 1KB BG Tile Map 0 (9800..9BFF)
 VRAM_MAP_B  = 0x1C00 # 1KB BG Tile Map 1 (9C00..9FFF)

Modified: pypy/trunk/pypy/lang/gameboy/test/test_video.py
==============================================================================
--- pypy/trunk/pypy/lang/gameboy/test/test_video.py	(original)
+++ pypy/trunk/pypy/lang/gameboy/test/test_video.py	Tue Mar  3 01:58:50 2009
@@ -47,7 +47,7 @@
     assert video.display == True
     assert video.v_blank == True
     assert video.dirty == True
-    assert len(video.vram) == constants.VRAM_SIZE
+    # assert len(video.vram) == constants.VRAM_SIZE
     assert len(video.oam) == constants.OAM_SIZE
     assert len(video.line) == 176
     assert len(video.objects) == constants.big_sprites
@@ -116,8 +116,6 @@
     value = 0xF6
     video.control.lcd_enabled = False
     
-    import pdb
-    pdb.set_trace()
     video.write(0xFF45, value)
     
     assert video.line_y_compare == value
@@ -511,3 +509,26 @@
     
     assert video.line == [0] * (8+160+8)
     
+def test_update_tile():
+    video = get_video()
+    assert len(video.tile_data_0) == constants.TILE_DATA_SIZE
+    assert len(video.tile_data_1) == constants.TILE_DATA_SIZE
+    for i in range(constants.TILE_DATA_SIZE * 2):
+        for j in range(0x100):
+            video.write(constants.TILE_DATA_ADDR + i, j)
+            assert video.read(constants.TILE_DATA_ADDR + i) == j
+
+def test_update_tile_map():
+    video = get_video()
+    assert len(video.tile_map_0) == constants.TILE_MAP_SIZE
+    assert len(video.tile_map_1) == constants.TILE_MAP_SIZE
+    for i in range(constants.TILE_MAP_SIZE * 2):
+        for j in range(0x100):
+            video.write(constants.TILE_MAP_ADDR + i, j)
+            assert video.read(constants.TILE_MAP_ADDR + i) == j
+
+def test_fill_tiles():
+    video = get_video()
+    for i in range(constants.TILE_DATA_SIZE * (2 * constants.SPRITE_SIZE) * 2):
+        video.write(constants.TILE_DATA_ADDR + i, 5)
+        assert video.read(constants.TILE_DATA_ADDR + i) == 5

Modified: pypy/trunk/pypy/lang/gameboy/test/test_video_registers.py
==============================================================================
--- pypy/trunk/pypy/lang/gameboy/test/test_video_registers.py	(original)
+++ pypy/trunk/pypy/lang/gameboy/test/test_video_registers.py	Tue Mar  3 01:58:50 2009
@@ -36,10 +36,10 @@
     control = get_control_register()
     
     control.background_and_window_lower_tile_data_selected = False
-    assert control.get_selected_tile_data_space() == constants.VRAM_DATA_B
+    assert control.get_selected_tile_data_space() == control.video.tile_data_1
     
     control.background_and_window_lower_tile_data_selected = True
-    assert control.get_selected_tile_data_space() == constants.VRAM_DATA_A
+    assert control.get_selected_tile_data_space() == control.video.tile_data_0
     
 # StatusRegister ---------------------------------------------------------------
 

Modified: pypy/trunk/pypy/lang/gameboy/video.py
==============================================================================
--- pypy/trunk/pypy/lang/gameboy/video.py	(original)
+++ pypy/trunk/pypy/lang/gameboy/video.py	Tue Mar  3 01:58:50 2009
@@ -1,12 +1,10 @@
 """
  PyGirl Emulator
- constants.LCD Video Display Processor
+ LCD Video Display Processor
 """
 import math
 import operator
-from pypy.lang.gameboy import constants
-from pypy.lang.gameboy.constants import SPRITE_SIZE, GAMEBOY_SCREEN_WIDTH, \
-                                        GAMEBOY_SCREEN_HEIGHT
+from pypy.lang.gameboy.constants import *
 from pypy.lang.gameboy.ram import iMemory
 from pypy.lang.gameboy.cpu import process_2s_complement
 from pypy.lang.gameboy.video_register import ControlRegister, StatusRegister
@@ -50,6 +48,7 @@
                                                       self.background)
         self.memory                 = memory
         self.create_tile_maps()
+        self.create_tiles()
         self.create_sprites()
         self.reset()
     
@@ -57,31 +56,51 @@
     
     def create_tile_maps(self):
         # create the maxumal possible sprites
-        self.tile_map_0 = self.create_tile_map(32 * 32)
-        self.tile_map_1 = self.create_tile_map(32 * 32)
+        self.tile_map_0 = self.create_tile_map()
+        self.tile_map_1 = self.create_tile_map()
         self.tile_maps = [self.tile_map_0, self.tile_map_1]
     
-    def create_tile_map(self, size):
-        tile_map = [None] * size
-        for i in range(size):
-            tile_map[i] = Tile(self)
-        return tile_map
+    def create_tile_map(self):
+        return [self.create_tile_group() for i in range(TILE_MAP_SIZE)]
+
+    def create_tile_group(self):
+        return [0x00 for i in range(TILE_GROUP_SIZE)]
+
+    def create_tiles(self):
+        self.tile_data_0 = self.create_tile_data()
+        self.tile_data_1 = self.create_tile_data()
+        self.tile_data = [self.tile_data_0, self.tile_data_1]
+
+    def create_tile_data(self):
+        return [Tile(i, self) for i in range(TILE_DATA_SIZE)]
         
     def update_tile(self, address, data):
-        # XXX to implement
-        #self.get_tile(address).set_data();
-        pass
+        self.get_tile(address).set_data_at(address, data);
     
     def get_tile(self, address):
-        # XXX to implement
-        pass
-    
-    def reset_all_tiles(self):
-        #for tile in self.tile_map_0:
-        #    tile.reset()
-        #for tile in self.tile_map_1:
-        #    tile.reset()
-        pass
+        tile_index = (address - TILE_DATA_ADDR) / (SPRITE_SIZE*2)
+        if tile_index < TILE_DATA_SIZE:
+            return self.tile_data_0[tile_index]
+        else:
+            return self.tile_data_1[tile_index - TILE_DATA_SIZE]
+
+    def select_tile_group_for(self, address):
+        tile_map_index = address - TILE_MAP_ADDR #) >> 1
+        if tile_map_index < TILE_MAP_SIZE * TILE_GROUP_SIZE:
+            map = self.tile_map_0
+        else:
+            map = self.tile_map_1
+            tile_map_index -= TILE_MAP_SIZE * TILE_GROUP_SIZE
+        tile_group = map[tile_map_index >> 5]
+        return tile_group, tile_map_index & 0x1F
+
+    def get_tile_map(self, address):
+        tile_group, group_index = self.select_tile_group_for(address)
+        return tile_group[group_index]
+
+    def update_tile_map(self, address, data):
+        tile_group, group_index = self.select_tile_group_for(address)
+        tile_group[group_index] = data
     
     # -----------------------------------------------------------------------
     def create_sprites(self):
@@ -106,16 +125,11 @@
             sprite.big_size = self.control.big_sprites 
        
     def get_sprite(self, address):
-        address -= constants.OAM_ADDR
+        address -= OAM_ADDR
         # address divided by 4 gives the correct sprite, each sprite has 4
         # bytes of attributes
         return self.sprites[ int(math.floor(address / 4)) ]
         
-    def reset_all_sprites(self):
-        #for sprite in self.sprites:
-        #    sprite.reset()
-        pass
-    
     # -----------------------------------------------------------------------
          
     def reset(self):
@@ -123,7 +137,7 @@
         self.status.reset()
         self.background.reset()
         self.window.reset()
-        self.cycles     = constants.MODE_2_TICKS
+        self.cycles     = MODE_2_TICKS
         self.line_y     = 0
         self.line_y_compare = 0
         self.dma        = 0xFF
@@ -137,15 +151,13 @@
         self.v_blank    = True
         self.dirty      = True
 
-        self.vram       = [0] * constants.VRAM_SIZE
-        self.reset_all_tiles()
+        # self.vram       = [0] * VRAM_SIZE
         # Object Attribute Memory
-        self.oam        = [0] * constants.OAM_SIZE
-        self.reset_all_sprites()
+        self.oam        = [0] * OAM_SIZE
         
         #XXX remove those dumb helper "shown_sprites"
         self.line       = [0] * (SPRITE_SIZE + GAMEBOY_SCREEN_WIDTH + SPRITE_SIZE)
-        self.shown_sprites    = [0] * constants.SPRITES_PER_LINE
+        self.shown_sprites    = [0] * SPRITES_PER_LINE
         self.palette    = [0] * 1024
         
         self.frames     = 0
@@ -156,68 +168,68 @@
     def write(self, address, data):
         address = int(address)
         # assert data >= 0x00 and data <= 0xFF
-        if address == constants.LCDC :
+        if address == LCDC :
             self.set_control(data)
-        elif address == constants.STAT:
+        elif address == STAT:
             self.set_status(data)
-        elif address == constants.SCY:
+        elif address == SCY:
             self.set_scroll_y(data)
-        elif address == constants.SCX:
+        elif address == SCX:
             self.set_scroll_x(data)
-        #elif address == constants.LY:
+        #elif address == LY:
         #    Read Only: line_y
         #    pass
-        elif address == constants.LYC:
+        elif address == LYC:
             self.set_line_y_compare(data)
-        elif address == constants.DMA:
+        elif address == DMA:
             self.set_dma(data)
-        elif address == constants.BGP:
+        elif address == BGP:
             self.set_background_palette(data)
-        elif address == constants.OBP0:
+        elif address == OBP0:
             self.set_object_palette_0(data)
-        elif address == constants.OBP1:
+        elif address == OBP1:
             self.set_object_palette_1(data)
-        elif address == constants.WY:
+        elif address == WY:
             self.set_window_y(data)
-        elif address == constants.WX:
+        elif address == WX:
             self.set_window_x(data)
-        elif constants.OAM_ADDR <= address < \
-        constants.OAM_ADDR + constants.OAM_SIZE:
+        elif OAM_ADDR <= address < \
+        OAM_ADDR + OAM_SIZE:
             self.set_oam(address, data)
-        elif constants.VRAM_ADDR <= address < \
-        constants.VRAM_ADDR + constants.VRAM_SIZE:
+        elif VRAM_ADDR <= address < \
+        VRAM_ADDR + VRAM_SIZE:
             self.set_vram(address, data)
             
     def read(self, address):
-        if address == constants.LCDC:
+        if address == LCDC:
             return self.get_control()
-        elif address == constants.STAT:
+        elif address == STAT:
             return self.get_status()
-        elif address == constants.SCY:
+        elif address == SCY:
             return self.get_scroll_y()
-        elif address == constants.SCX:
+        elif address == SCX:
             return self.get_scroll_x()
-        elif address == constants.LY:
+        elif address == LY:
             return self.get_line_y()
-        elif address == constants.LYC:
+        elif address == LYC:
             return self.get_line_y_compare()
-        elif address == constants.DMA:
+        elif address == DMA:
             return self.get_dma()
-        elif address == constants.BGP:
+        elif address == BGP:
             return self.get_background_palette()
-        elif address == constants.OBP0:
+        elif address == OBP0:
             return self.get_object_palette_0()
-        elif address == constants.OBP1:
+        elif address == OBP1:
             return self.get_object_palette_1()
-        elif address == constants.WY:
+        elif address == WY:
             return self.get_window_y()
-        elif address == constants.WX:
+        elif address == WX:
             return self.get_window_x()
-        elif constants.OAM_ADDR <= address < \
-        constants.OAM_ADDR + constants.OAM_SIZE:
+        elif OAM_ADDR <= address < \
+        OAM_ADDR + OAM_SIZE:
             return self.get_oam(address)
-        elif constants.VRAM_ADDR <= address < \
-        constants.VRAM_ADDR + constants.VRAM_SIZE:
+        elif VRAM_ADDR <= address < \
+        VRAM_ADDR + VRAM_SIZE:
             return self.get_vram(address)
         return 0xFF
 
@@ -329,7 +341,7 @@
         """
         self.dma = data
         # copy the memory region
-        for index in range(constants.OAM_SIZE):
+        for index in range(OAM_SIZE):
             self.oam[index] = self.memory.read((self.dma << 8) + index)
         self.update_all_sprites()
 
@@ -410,23 +422,27 @@
         attributes of the sprites, this works only during the v-blank and
         the h-blank period.
         """
-        self.oam[address - constants.OAM_ADDR] = data & 0xFF
+        self.oam[address - OAM_ADDR] = data & 0xFF
         self.update_sprite(address, data)
         
     def get_oam(self, address):
         return self.get_sprite(address).get_data_at(address);
         
     def set_vram(self, address, data):
-       """
-       sets one byte of the video memory.
-       The video memory contains the tiles used to display.
-       """
-       self.vram[address - constants.VRAM_ADDR] = data & 0xFF
-       self.update_tile(address, data)
+        """
+        sets one byte of the video memory.
+        The video memory contains the tiles used to display.
+        """
+        if address < TILE_MAP_ADDR:
+            self.update_tile(address, data)
+        else:
+            self.update_tile_map(address, data)
     
     def get_vram(self, address):
-        #return self.get_tile(address).get_data()[address % 4]
-        return self.vram[address - constants.VRAM_ADDR]
+        if address < TILE_MAP_ADDR:
+            return self.get_tile(address).get_data_at(address)
+        else:
+            return self.get_tile_map(address)
     
     # emulation ----------------------------------------------------------------
 
@@ -478,7 +494,7 @@
             if sprite.is_shown_on_current_line(self):
                 self.shown_sprites[count] = sprite
                 count += 1
-                if count >= constants.SPRITES_PER_LINE:
+                if count >= SPRITES_PER_LINE:
                     break
         self.sort_scan_sprite(count)
         return count
@@ -494,23 +510,21 @@
             self.shown_sprites[index], self.shown_sprites[highest] = \
                     self.shown_sprites[highest], self.shown_sprites[index]
 
-    def draw_tiles(self, x, tile_map, tile_data):
+    def draw_tiles(self, x_start, tile_group, y, group_index=0):
+        x = x_start
+        tile_data = self.control.get_selected_tile_data_space()
         while x < GAMEBOY_SCREEN_WIDTH+SPRITE_SIZE:
-            tile = self.vram[tile_map]
-            assert tile == (tile & 0xFF)
+            tile_index = tile_group[group_index % TILE_GROUP_SIZE]
             if not self.control.background_and_window_lower_tile_data_selected:
-                tile = (tile ^ 0x80) & 0xFF
-            self.draw_tile(x, tile_data + (tile << 4))
-            tile_map = (tile_map & 0x1FE0) + ((tile_map + 1) & 0x001F)
+                tile_index ^= 0x80
+            tile = tile_data[tile_index]
+            tile.draw(x, y)
+            group_index += 1
             x += SPRITE_SIZE
      
-    def draw_tile(self, x, address):
-        pattern =  self.get_pattern(address)
-        for i in range(0, SPRITE_SIZE):
-            self.line[x + i] = (pattern >> (7-i)) & 0x0101
-                   
     def get_pattern(self, address):
-        return self.vram[address] + (self.vram[address + 1] << 8)
+        # TODO: remove method!
+        return self.get_vram(VRAM_ADDR + address) + (self.get_vram(VRAM_ADDR + address + 1) << 8)
 
     def draw_object_tile(self, sprite):
         self.draw_object(set_tile_line_call_wrapper(self), sprite)
@@ -587,7 +601,7 @@
                 color = (self.object_palette_1 >> ((((pattern >> 4) & 0x02) +\
                         ((pattern >> 1) & 0x01)) << 1)) & 0x03
             index = ((pattern & 0x30) << 4) + (pattern & 0x0F)
-            #self.palette[index] = constants.COLOR_MAP[color]
+            #self.palette[index] = COLOR_MAP[color]
             self.palette[index] = color
         self.dirty = False
 
@@ -596,8 +610,8 @@
 class VideoDriver(object):
     
     def __init__(self):
-        self.width = int(constants.GAMEBOY_SCREEN_WIDTH)
-        self.height = int(constants.GAMEBOY_SCREEN_HEIGHT)
+        self.width = int(GAMEBOY_SCREEN_WIDTH)
+        self.height = int(GAMEBOY_SCREEN_HEIGHT)
         self.clear_pixels()
         
     def clear_pixels(self):

Modified: pypy/trunk/pypy/lang/gameboy/video_register.py
==============================================================================
--- pypy/trunk/pypy/lang/gameboy/video_register.py	(original)
+++ pypy/trunk/pypy/lang/gameboy/video_register.py	Tue Mar  3 01:58:50 2009
@@ -144,10 +144,10 @@
         self.background.enabled                      = bool(value & (1 << 0))
 
         if previous_big_sprites != self.big_sprites:
-            video.update_sprite_size()
+            self.video.update_sprite_size()
         
     def get_selected_tile_data_space(self):
         if self.background_and_window_lower_tile_data_selected:
-            return constants.VRAM_DATA_A
+            return self.video.tile_data_0
         else:
-            return constants.VRAM_DATA_B
+            return self.video.tile_data_1

Modified: pypy/trunk/pypy/lang/gameboy/video_sprite.py
==============================================================================
--- pypy/trunk/pypy/lang/gameboy/video_sprite.py	(original)
+++ pypy/trunk/pypy/lang/gameboy/video_sprite.py	Tue Mar  3 01:58:50 2009
@@ -153,7 +153,7 @@
             return self.get_tile_number()
             
     def get_tile_address(self, video):
-        return (self.get_tile(video) << 4) +  (self.get_draw_y(video) << 1)
+        return (self.get_tile(video) << 4) + (self.get_draw_y(video) << 1)
         
     def get_draw_y(self, video):
         y = self.current_line_y(video)
@@ -194,43 +194,60 @@
             self.y = self.get_height() - 1 - self.y
     
 # -----------------------------------------------------------------------------
-    
-    
+
+
 class Tile(object):
     
-    def __init__(self, video):
+    def __init__(self, number, video):
         self.video = video
+        self.number = number
         self.reset()
+        self.data = [0x00 for i in range(2*SPRITE_SIZE)]
+
+    def draw(self, x, y):
+        pattern = self.get_pattern_at(y << 1)
+        for i in range(SPRITE_SIZE):
+            value = (pattern >> (SPRITE_SIZE - 1 - i)) & 0x0101
+            self.video.line[x + i] = value
         
     def reset(self):
         pass
     
     def set_tile_data(self, data):
-        pass
+        self.data = data
 
-    def get_pixel_data(self):
-        return self.pixel_data
-    
-    def get_selected_tile_map_space(self):
-        pass
-    
     def get_data_at(self, address):
-        return self.get_data()[address % self.byte_size()]
+        return self.data[address % (2*SPRITE_SIZE)]
     
-    def get_data():
-        return []
+    def set_data_at(self, address, data):
+        self.data[address % (2*SPRITE_SIZE)] = data
     
-    def byte_size(self):
-        return 0
+    def get_data(self):
+        return self.data
+
+    def get_pattern_at(self, address):
+        return self.get_data_at(address) +\
+               (self.get_data_at(address + 1) << 8)
     
 # -----------------------------------------------------------------------------
 
-class Window(object):
-    
+class Drawable(object):
     def __init__(self, video):
         self.video = video
         self.reset()
 
+    def get_tile_map_space(self):
+        if self.upper_tile_map_selected:
+            return self.video.tile_map_1
+        else:
+            return self.video.tile_map_0
+
+    def reset(self):
+        raise Exception("Not implemented")
+ 
+
+class Window(Drawable):
+    
     def reset(self):
         self.x       = 0
         self.y       = 0
@@ -241,36 +258,21 @@
     def switch_on(self):
         if self.line_y == 0 and self.video.line_y > self.y:
             self.line_y = GAMEBOY_SCREEN_HEIGHT
-    
-    def get_tile_map_space(self):
-        #if (self.control.read() & mask) != 0:
-        if self.upper_tile_map_selected:
-            return constants.VRAM_MAP_B
-        else:
-            return constants.VRAM_MAP_A
-        
+       
     def draw_line(self, line_y):
-        if line_y  >= self.y and self.x < 167 and \
+        if line_y >= self.y and self.x < GAMEBOY_SCREEN_WIDTH+SPRITE_SIZE-1 and \
            self.line_y < GAMEBOY_SCREEN_HEIGHT:
-            tile_map, tile_data = self.prepare_window_data()
-            self.video.draw_tiles(self.x + 1, tile_map, tile_data)
+
+            tile_map   = self.get_tile_map_space()
+            tile_group = tile_map[self.line_y >> 5]
+
+            self.video.draw_tiles(self.x + 1, tile_group, self.line_y)
             self.line_y += 1
 
-    def prepare_window_data(self):
-        tile_map   = self.get_tile_map_space()
-        tile_map  += (self.line_y >> 3) << 5
-        tile_data  = self.video.control.get_selected_tile_data_space()
-        tile_data += (self.line_y & 7) << 1
-        return tile_map, tile_data;
-        
 # -----------------------------------------------------------------------------
 
-class Background(object):
+class Background(Drawable):
     
-    def __init__(self, video):
-        self.video = video
-        self.reset()
-        
     def reset(self):
         # SCROLLX and SCROLLY hold the coordinates of background to
         # be displayed in the left upper corner of the screen.
@@ -279,28 +281,15 @@
         self.enabled    = True
         self.upper_tile_map_selected = False
       
-    def get_tile_map_space(self):
-        #if (self.control.read() & mask) != 0:
-        if self.upper_tile_map_selected:
-            return constants.VRAM_MAP_B
-        else:
-            return constants.VRAM_MAP_A
-          
     def draw_clean_line(self, line_y):
-        for x in range(8+GAMEBOY_SCREEN_WIDTH+8):
+        for x in range(SPRITE_SIZE+GAMEBOY_SCREEN_WIDTH+SPRITE_SIZE):
             self.video.line[x] = 0x00
     
     def draw_line(self, line_y):
-        y = (self.scroll_y + line_y) & 0xFF
-        x = self.scroll_x            & 0xFF
-        tile_map, tile_data = self.prepare_background_data(x, y)
-        self.video.draw_tiles(8 - (x & 7), tile_map, tile_data)
-        
-    def prepare_background_data(self, x, y):
-        tile_map   = self.get_tile_map_space()
-        tile_map  += ((y >> 3) << 5) + (x >> 3)
-        tile_data  = self.video.control.get_selected_tile_data_space()
-        tile_data += (y & 7) << 1
-        return tile_map, tile_data
-    
-      
+        y = self.scroll_y + line_y
+        x = self.scroll_x
+
+        tile_map = self.get_tile_map_space()
+        tile_group = tile_map[y >> 3]
+        # print "Background"
+        self.video.draw_tiles(8 - (x % 8), tile_group, y, x >> 3)



More information about the Pypy-commit mailing list