all repos — mgba @ f3efd3726470ca3e46209a58e8443f80cc891f3c

mGBA Game Boy Advance Emulator

src/platform/python/mgba/gb.py (view raw)

  1# Copyright (c) 2013-2017 Jeffrey Pfau
  2#
  3# This Source Code Form is subject to the terms of the Mozilla Public
  4# License, v. 2.0. If a copy of the MPL was not distributed with this
  5# file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6from ._pylib import ffi, lib  # pylint: disable=no-name-in-module
  7from .lr35902 import LR35902Core
  8from .core import Core, needs_reset
  9from .memory import Memory
 10from .tile import Sprite
 11from . import create_callback
 12
 13
 14class GB(Core):
 15    KEY_A = lib.GBA_KEY_A
 16    KEY_B = lib.GBA_KEY_B
 17    KEY_SELECT = lib.GBA_KEY_SELECT
 18    KEY_START = lib.GBA_KEY_START
 19    KEY_DOWN = lib.GBA_KEY_DOWN
 20    KEY_UP = lib.GBA_KEY_UP
 21    KEY_LEFT = lib.GBA_KEY_LEFT
 22    KEY_RIGHT = lib.GBA_KEY_RIGHT
 23
 24    def __init__(self, native):
 25        super(GB, self).__init__(native)
 26        self._native = ffi.cast("struct GB*", native.board)
 27        self.sprites = GBObjs(self)
 28        self.cpu = LR35902Core(self._core.cpu)
 29        self.memory = None
 30
 31    @needs_reset
 32    def _init_cache(self, cache):
 33        lib.GBVideoCacheInit(cache)
 34        lib.GBVideoCacheAssociate(cache, ffi.addressof(self._native.video))
 35
 36    def _deinit_cache(self, cache):
 37        lib.mCacheSetDeinit(cache)
 38        if self._was_reset:
 39            self._native.video.renderer.cache = ffi.NULL
 40
 41    def _load(self):
 42        super(GB, self)._load()
 43        self.memory = GBMemory(self._core)
 44
 45    def attach_sio(self, link):
 46        lib.GBSIOSetDriver(ffi.addressof(self._native.sio), link._native)
 47
 48    def __del__(self):
 49        lib.GBSIOSetDriver(ffi.addressof(self._native.sio), ffi.NULL)
 50
 51
 52create_callback("GBSIOPythonDriver", "init")
 53create_callback("GBSIOPythonDriver", "deinit")
 54create_callback("GBSIOPythonDriver", "writeSB")
 55create_callback("GBSIOPythonDriver", "writeSC")
 56
 57
 58class GBSIODriver(object):
 59    def __init__(self):
 60        self._handle = ffi.new_handle(self)
 61        self._native = ffi.gc(lib.GBSIOPythonDriverCreate(self._handle), lib.free)
 62
 63    def init(self):
 64        return True
 65
 66    def deinit(self):
 67        pass
 68
 69    def write_sb(self, value):
 70        pass
 71
 72    def write_sc(self, value):
 73        return value
 74
 75
 76class GBSIOSimpleDriver(GBSIODriver):
 77    def __init__(self, period=0x100):
 78        super(GBSIOSimpleDriver, self).__init__()
 79        self.rx = 0x00  # pylint: disable=invalid-name
 80        self._period = period
 81
 82    def init(self):
 83        self._native.p.period = self._period
 84        return True
 85
 86    def write_sb(self, value):
 87        self.rx = value  # pylint: disable=invalid-name
 88
 89    def write_sc(self, value):
 90        self._native.p.period = self._period
 91        if value & 0x80:
 92            lib.mTimingDeschedule(ffi.addressof(self._native.p.p.timing), ffi.addressof(self._native.p.event))
 93            lib.mTimingSchedule(ffi.addressof(self._native.p.p.timing), ffi.addressof(self._native.p.event), self._native.p.period)
 94        return value
 95
 96    def is_ready(self):
 97        return not self._native.p.remainingBits
 98
 99    @property
100    def tx(self):  # pylint: disable=invalid-name
101        return self._native.p.pendingSB
102
103    @property
104    def period(self):
105        return self._native.p.period
106
107    @tx.setter
108    def tx(self, newTx):  # pylint: disable=invalid-name
109        self._native.p.pendingSB = newTx
110        self._native.p.remainingBits = 8
111
112    @period.setter
113    def period(self, new_period):
114        self._period = new_period
115        if self._native.p:
116            self._native.p.period = new_period
117
118
119class GBMemory(Memory):
120    def __init__(self, core):
121        super(GBMemory, self).__init__(core, 0x10000)
122
123        self.cart = Memory(core, lib.GB_SIZE_CART_BANK0 * 2, lib.GB_BASE_CART_BANK0)
124        self.vram = Memory(core, lib.GB_SIZE_VRAM, lib.GB_BASE_VRAM)
125        self.sram = Memory(core, lib.GB_SIZE_EXTERNAL_RAM, lib.GB_REGION_EXTERNAL_RAM)
126        self.iwram = Memory(core, lib.GB_SIZE_WORKING_RAM_BANK0, lib.GB_BASE_WORKING_RAM_BANK0)
127        self.oam = Memory(core, lib.GB_SIZE_OAM, lib.GB_BASE_OAM)
128        self.io = Memory(core, lib.GB_SIZE_IO, lib.GB_BASE_IO)  # pylint: disable=invalid-name
129        self.hram = Memory(core, lib.GB_SIZE_HRAM, lib.GB_BASE_HRAM)
130
131
132class GBSprite(Sprite):
133    PALETTE_BASE = (8,)
134
135    def __init__(self, obj, core):
136        self.x = obj.x  # pylint: disable=invalid-name
137        self.y = obj.y  # pylint: disable=invalid-name
138        self.tile = obj.tile
139        self._attr = obj.attr
140        self.width = 8
141        lcdc = core._native.memory.io[0x40]
142        self.height = 16 if lcdc & 4 else 8
143        if core._native.model >= lib.GB_MODEL_CGB:
144            if self._attr & 8:
145                self.tile += 512
146            self.palette_id = self._attr & 7
147        else:
148            self.palette_id = (self._attr >> 4) & 1
149        self.palette_id += 8
150
151
152class GBObjs:
153    def __init__(self, core):
154        self._core = core
155        self._obj = core._native.video.oam.obj
156
157    def __len__(self):
158        return 40
159
160    def __getitem__(self, index):
161        if index >= len(self):
162            raise IndexError()
163        sprite = GBSprite(self._obj[index], self._core)
164        sprite.constitute(self._core.tiles[0], 0)
165        return sprite