src/platform/python/mgba/gba.py (view raw)
1# Copyright (c) 2013-2016 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 .arm import ARMCore
8from .core import Core, needs_reset
9from .tile import Sprite
10from .memory import Memory
11from . import create_callback
12
13
14class GBA(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 KEY_L = lib.GBA_KEY_L
24 KEY_R = lib.GBA_KEY_R
25
26 SIO_NORMAL_8 = lib.SIO_NORMAL_8
27 SIO_NORMAL_32 = lib.SIO_NORMAL_32
28 SIO_MULTI = lib.SIO_MULTI
29 SIO_UART = lib.SIO_UART
30 SIO_JOYBUS = lib.SIO_JOYBUS
31 SIO_GPIO = lib.SIO_GPIO
32
33 def __init__(self, native):
34 super(GBA, self).__init__(native)
35 self._native = ffi.cast("struct GBA*", native.board)
36 self.sprites = GBAObjs(self)
37 self.cpu = ARMCore(self._core.cpu)
38 self.memory = None
39 self._sio = set()
40
41 @needs_reset
42 def _init_cache(self, cache):
43 lib.GBAVideoCacheInit(cache)
44 lib.GBAVideoCacheAssociate(cache, ffi.addressof(self._native.video))
45
46 def _deinit_cache(self, cache):
47 lib.mCacheSetDeinit(cache)
48 if self._was_reset:
49 self._native.video.renderer.cache = ffi.NULL
50
51 def _load(self):
52 super(GBA, self)._load()
53 self.memory = GBAMemory(self._core, self._native.memory.romSize)
54
55 def attach_sio(self, link, mode=lib.SIO_MULTI):
56 self._sio.add(mode)
57 lib.GBASIOSetDriver(ffi.addressof(self._native.sio), link._native, mode)
58
59 def __del__(self):
60 for mode in self._sio:
61 lib.GBASIOSetDriver(ffi.addressof(self._native.sio), ffi.NULL, mode)
62
63
64create_callback("GBASIOPythonDriver", "init")
65create_callback("GBASIOPythonDriver", "deinit")
66create_callback("GBASIOPythonDriver", "load")
67create_callback("GBASIOPythonDriver", "unload")
68create_callback("GBASIOPythonDriver", "writeRegister")
69
70
71class GBASIODriver(object):
72 def __init__(self):
73 super(GBASIODriver, self).__init__()
74
75 self._handle = ffi.new_handle(self)
76 self._native = ffi.gc(lib.GBASIOPythonDriverCreate(self._handle), lib.free)
77
78 def init(self):
79 return True
80
81 def deinit(self):
82 pass
83
84 def load(self):
85 return True
86
87 def unload(self):
88 return True
89
90 def write_register(self, address, value):
91 return value
92
93
94class GBASIOJOYDriver(GBASIODriver):
95 RESET = lib.JOY_RESET
96 POLL = lib.JOY_POLL
97 TRANS = lib.JOY_TRANS
98 RECV = lib.JOY_RECV
99
100 def __init__(self):
101 super(GBASIOJOYDriver, self).__init__()
102
103 self._native = ffi.gc(lib.GBASIOJOYPythonDriverCreate(self._handle), lib.free)
104
105 def send_command(self, cmd, data):
106 buffer = ffi.new('uint8_t[5]')
107 try:
108 buffer[0] = data[0]
109 buffer[1] = data[1]
110 buffer[2] = data[2]
111 buffer[3] = data[3]
112 buffer[4] = data[4]
113 except IndexError:
114 pass
115
116 outlen = lib.GBASIOJOYSendCommand(self._native, cmd, buffer)
117 if outlen > 0 and outlen <= 5:
118 return bytes(buffer[0:outlen])
119 return None
120
121
122class GBAMemory(Memory):
123 def __init__(self, core, romSize=lib.SIZE_CART0):
124 super(GBAMemory, self).__init__(core, 0x100000000)
125
126 self.bios = Memory(core, lib.SIZE_BIOS, lib.BASE_BIOS)
127 self.wram = Memory(core, lib.SIZE_WORKING_RAM, lib.BASE_WORKING_RAM)
128 self.iwram = Memory(core, lib.SIZE_WORKING_IRAM, lib.BASE_WORKING_IRAM)
129 self.io = Memory(core, lib.SIZE_IO, lib.BASE_IO) # pylint: disable=invalid-name
130 self.palette = Memory(core, lib.SIZE_PALETTE_RAM, lib.BASE_PALETTE_RAM)
131 self.vram = Memory(core, lib.SIZE_VRAM, lib.BASE_VRAM)
132 self.oam = Memory(core, lib.SIZE_OAM, lib.BASE_OAM)
133 self.cart0 = Memory(core, romSize, lib.BASE_CART0)
134 self.cart1 = Memory(core, romSize, lib.BASE_CART1)
135 self.cart2 = Memory(core, romSize, lib.BASE_CART2)
136 self.cart = self.cart0
137 self.rom = self.cart0
138 self.sram = Memory(core, lib.SIZE_CART_SRAM, lib.BASE_CART_SRAM)
139
140
141class GBASprite(Sprite):
142 TILE_BASE = 0x800, 0x400
143 PALETTE_BASE = 0x10, 1
144
145 def __init__(self, obj):
146 self._a = obj.a
147 self._b = obj.b
148 self._c = obj.c
149 self.x = self._b & 0x1FF # pylint: disable=invalid-name
150 self.y = self._a & 0xFF # pylint: disable=invalid-name
151 self._shape = self._a >> 14
152 self._size = self._b >> 14
153 self._256_color = bool(self._a & 0x2000)
154 self.width, self.height = lib.GBAVideoObjSizes[self._shape * 4 + self._size]
155 self.tile = self._c & 0x3FF
156 if self._256_color:
157 self.palette_id = 0
158 self.tile >>= 1
159 else:
160 self.palette_id = self._c >> 12
161
162
163class GBAObjs:
164 def __init__(self, core):
165 self._core = core
166 self._obj = core._native.video.oam.obj
167
168 def __len__(self):
169 return 128
170
171 def __getitem__(self, index):
172 if index >= len(self):
173 raise IndexError()
174 sprite = GBASprite(self._obj[index])
175 tiles = self._core.tiles[3 if sprite._256_color else 2]
176 map_1d = bool(self._core._native.memory.io[0] & 0x40)
177 sprite.constitute(tiles, 0 if map_1d else 0x20)
178 return sprite