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