all repos — mgba @ 20506226c9f73791bf4070ae2fcb63e4ee18dec9

mGBA Game Boy Advance Emulator

src/platform/python/mgba/memory.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
  7
  8class MemoryView(object):
  9    def __init__(self, core, width, size, base=0, sign="u"):
 10        self._core = core
 11        self._width = width
 12        self._size = size
 13        self._base = base
 14        self._busRead = getattr(self._core, "busRead" + str(width * 8))
 15        self._busWrite = getattr(self._core, "busWrite" + str(width * 8))
 16        self._rawRead = getattr(self._core, "rawRead" + str(width * 8))
 17        self._rawWrite = getattr(self._core, "rawWrite" + str(width * 8))
 18        self._mask = (1 << (width * 8)) - 1 # Used to force values to fit within range so that negative values work
 19        if sign == "u" or sign == "unsigned":
 20            self._type = "uint{}_t".format(width * 8)
 21        elif sign == "i" or sign == "s" or sign == "signed":
 22            self._type = "int{}_t".format(width * 8)
 23        else:
 24            raise ValueError("Invalid sign type: '{}'".format(sign))
 25
 26    def _addrCheck(self, address):
 27        if isinstance(address, slice):
 28            start = address.start or 0
 29            stop = self._size - self._width if address.stop is None else address.stop
 30        else:
 31            start = address
 32            stop = address + self._width
 33        if start >= self._size or stop > self._size:
 34            raise IndexError()
 35        if start < 0 or stop < 0:
 36            raise IndexError()
 37
 38    def __len__(self):
 39        return self._size
 40
 41    def __getitem__(self, address):
 42        self._addrCheck(address)
 43        if isinstance(address, slice):
 44            start = address.start or 0
 45            stop = self._size - self._width if address.stop is None else address.stop
 46            step = address.step or self._width
 47            return [int(ffi.cast(self._type, self._busRead(self._core, self._base + a))) for a in range(start, stop, step)]
 48        else:
 49            return int(ffi.cast(self._type, self._busRead(self._core, self._base + address)))
 50
 51    def __setitem__(self, address, value):
 52        self._addrCheck(address)
 53        if isinstance(address, slice):
 54            start = address.start or 0
 55            stop = self._size - self._width if address.stop is None else address.stop
 56            step = address.step or self._width
 57            for a in range(start, stop, step):
 58                self._busWrite(self._core, self._base + a, value[a] & self._mask)
 59        else:
 60            self._busWrite(self._core, self._base + address, value & self._mask)
 61
 62    def rawRead(self, address, segment=-1):
 63        self._addrCheck(address)
 64        return int(ffi.cast(self._type, self._rawRead(self._core, self._base + address, segment)))
 65
 66    def rawWrite(self, address, value, segment=-1):
 67        self._addrCheck(address)
 68        self._rawWrite(self._core, self._base + address, segment, value & self._mask)
 69
 70
 71class MemorySearchResult(object):
 72    def __init__(self, memory, result):
 73        self.address = result.address
 74        self.segment = result.segment
 75        self.guessDivisor = result.guessDivisor
 76        self.type = result.type
 77
 78        if result.type == Memory.SEARCH_8:
 79            self._memory = memory.u8
 80        elif result.type == Memory.SEARCH_16:
 81            self._memory = memory.u16
 82        elif result.type == Memory.SEARCH_32:
 83            self._memory = memory.u32
 84        elif result.type == Memory.SEARCH_STRING:
 85            self._memory = memory.u8
 86        else:
 87            raise ValueError("Unknown type: %X" % result.type)
 88
 89    @property
 90    def value(self):
 91        if self.type == Memory.SEARCH_STRING:
 92            raise ValueError
 93        return self._memory[self.address] * self.guessDivisor
 94
 95    @value.setter
 96    def value(self, v):
 97        if self.type == Memory.SEARCH_STRING:
 98            raise IndexError
 99        self._memory[self.address] = v // self.guessDivisor
100
101
102class Memory(object):
103    SEARCH_INT = lib.mCORE_MEMORY_SEARCH_INT
104    SEARCH_STRING = lib.mCORE_MEMORY_SEARCH_STRING
105    SEARCH_GUESS = lib.mCORE_MEMORY_SEARCH_GUESS
106
107    SEARCH_EQUAL = lib.mCORE_MEMORY_SEARCH_EQUAL
108
109    READ = lib.mCORE_MEMORY_READ
110    WRITE = lib.mCORE_MEMORY_READ
111    RW = lib.mCORE_MEMORY_RW
112
113    def __init__(self, core, size, base=0):
114        self.size = size
115        self.base = base
116        self._core = core
117
118        self.u8 = MemoryView(core, 1, size, base, "u")
119        self.u16 = MemoryView(core, 2, size, base, "u")
120        self.u32 = MemoryView(core, 4, size, base, "u")
121        self.s8 = MemoryView(core, 1, size, base, "s")
122        self.s16 = MemoryView(core, 2, size, base, "s")
123        self.s32 = MemoryView(core, 4, size, base, "s")
124
125    def __len__(self):
126        return self._size
127
128    def search(self, value, type=SEARCH_GUESS, flags=RW, limit=10000, old_results=[]):
129        results = ffi.new("struct mCoreMemorySearchResults*")
130        lib.mCoreMemorySearchResultsInit(results, len(old_results))
131        params = ffi.new("struct mCoreMemorySearchParams*")
132        params.memoryFlags = flags
133        params.type = type
134        params.op = self.SEARCH_EQUAL
135        if type == self.SEARCH_INT:
136            params.valueInt = int(value)
137        else:
138            params.valueStr = ffi.new("char[]", str(value).encode("ascii"))
139
140        for result in old_results:
141            r = lib.mCoreMemorySearchResultsAppend(results)
142            r.address = result.address
143            r.segment = result.segment
144            r.guessDivisor = result.guessDivisor
145            r.type = result.type
146        if old_results:
147            lib.mCoreMemorySearchRepeat(self._core, params, results)
148        else:
149            lib.mCoreMemorySearch(self._core, params, results, limit)
150        new_results = [MemorySearchResult(self, lib.mCoreMemorySearchResultsGetPointer(results, i)) for i in range(lib.mCoreMemorySearchResultsSize(results))]
151        lib.mCoreMemorySearchResultsDeinit(results)
152        return new_results
153
154    def __getitem__(self, address):
155        if isinstance(address, slice):
156            return bytearray(self.u8[address])
157        else:
158            return self.u8[address]