| Разработка и ромхакинг > Ромхакинг и программирование |
| Вопрос для программистов разбирающихся в эмуляции |
| (1/1) |
| pristavkin:
У меня есть исходные коды 3-х разных эмуляторов И везде есть одни очень схожие конструкции, которые не могу понять Речь идёт о чтении опкода записи в память и чтения из памяти вот inline byte readopcode(register unsigned short address) { return memory_mapped[address>>12][address&0x0FFF]; } inline unsigned short readword(register unsigned short address) { return (unsigned short)(memory_mapped[address>>12][address&0x0FFF]|(memory_mapped[(address+1)>>12][(address+1)&0x0FFF]<<8)); } inline void writeword(register unsigned short address, register unsigned short data) { memory_mapped[address>>12][address&0x0FFF] = data; memory_mapped[((address+1)&0xFFFF)>>12][(address+1)&0x0FFF] = (data>>8); } INLINE void bank_writebyte(register uint32 address, register uint8 value) { cpu.mem_page[address >> NES6502_BANKSHIFT][address & NES6502_BANKMASK] = value; } #define NES6502_BANKSHIFT 12 uint8 *mem_page[NES6502_NUMBANKS]; /* memory page pointers */ Не могу понять почему во всех трёх эмуляторрах идёт сдвиг на 12 и почему используются 2 массив, если он только один. Или я не силён в арифметике с++? Указателях? Объясните. Что значат эти конструкции Зачем мы здвигаем да ещё и применяем логическую операцию с FFF [((address+1)&0xFFFF)>>12? Почему именно на 12? |
| Rumata:
Указал бы хоть, о какой платформе речь. И, думаю, это не в Железо, а в Ромхакинг и программирование |
| GManiac:
0x0FFF - это маска из 12 бит, т.е. она оставляет младшие 12 бит и отсекает старшие. "address>>12" сдвигает число на 12 бит вправо. Таким образом return memory_mapped[address>>12][address&0x0FFF]; по сути отображает 16-битный адрес в 2 числа: старшие 4 бита и младшие 12 бит. Т.е. разбивает адресное пространство 64 к на блоки по 4 к. Зачем это сделано - видать, для НЕС так надо. Добавлено позже: Вторая и третья функции - тоже понятно, работа со словами, поэтому "data>>8" в записи и мемори_мэппед в чтении. А чуть ниже объясняется, что 12 берётся как стандартный размер страницы маппера. Точнее, 2^12. #define NES6502_BANKSHIFT 12 Я уверен, там где-то должно быть описано, что NES6502_NUMBANKS = 16 - NES6502_BANKSHIFT. |
| pristavkin:
Это всё взято из примеров jnes, gens, и gbuboy Добавлено позже: INLINE uint32 zp_readword(register uint8 address) { #ifdef HOST_LITTLE_ENDIAN /* TODO: this fails if host architecture doesn't support byte alignment */ return (uint32) (*(uint16 *)(ram + address)); #else #ifdef TARGET_CPU_PPC return __lhbrx(ram, address); #else uint32 x = (uint32) *(uint16 *)(ram + address); return (x << 8) | (x >> 8); #endif /* TARGET_CPU_PPC */ #endif /* HOST_LITTLE_ENDIAN */ } INLINE uint8 bank_readbyte(register uint32 address) { return cpu.mem_page[address >> NES6502_BANKSHIFT][address & NES6502_BANKMASK]; } INLINE uint32 bank_readword(register uint32 address) { #ifdef HOST_LITTLE_ENDIAN /* TODO: this fails if src address is $xFFF */ /* TODO: this fails if host architecture doesn't support byte alignment */ return (uint32) (*(uint16 *)(cpu.mem_page[address >> NES6502_BANKSHIFT] + (address & NES6502_BANKMASK))); #else #ifdef TARGET_CPU_PPC return __lhbrx(cpu.mem_page[address >> NES6502_BANKSHIFT], address & NES6502_BANKMASK); #else uint32 x = (uint32) *(uint16 *)(cpu.mem_page[address >> NES6502_BANKSHIFT] + (address & NES6502_BANKMASK)); return (x << 8) | (x >> 8); #endif /* TARGET_CPU_PPC */ #endif /* HOST_LITTLE_ENDIAN */ } INLINE void bank_writebyte(register uint32 address, register uint8 value) { cpu.mem_page[address >> NES6502_BANKSHIFT][address & NES6502_BANKMASK] = value; } Хорошо, вот это пример - да для денди. А первый из z80 для сеги. Почему там тоже на 12 memory_mapped[address>>12][address&0x0FFF]; Можно пример привести с цифрами и адресами. Чтобы понятней было И так н могу понять почему тут 2 массива. Если он вначале объявлен как олин массив казателей. В обещм здесь какая то тонколсть есть которую я понять не могу Добавлено позже: #define NES6502_BANKSHIFT 13 Ну почти угадал |
| GManiac:
Почему они так делают - спрашивай у авторов. В случае НЕС можно было бы предположить, что это сделано для облегчения банкования... не знаю, но раз для Z80 такое написано, то непонятно. Примеры: address = 0x2345 0x2345 >> 12 = 0x0002 (старшие 4 бита) 0x2345 & 0x0FFF = 0x0345 (младшие 12 бит) Это стандартные приёмы в двоичной арифметике. return memory_mapped[address>>12][address&0x0FFF]; превращается в return memory_mapped[0x0002][0x0345]; или для удобства return memory_mapped[2][345]; |
| pristavkin:
А ещё раз почему именно 12 а не 8 или 16? |
| pristavkin:
^_^ |
| CrazyMax:
--- Цитата: pristavkin от 25 Сентябрь 2009, 17:41:28 --- ^_^ --- Конец цитаты --- Выучи логику и вопросы самы по себе отпадут :) |
| Навигация |
| Главная страница сообщений |