Автор Тема: Вопрос для программистов разбирающихся в эмуляции  (Прочитано 3897 раз)

0 Пользователей и 1 Гость просматривают эту тему.

Оффлайн pristavkin

  • Пользователь
  • Сообщений: 13
    • Просмотр профиля
У меня есть исходные коды 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

  • Супермодератор
  • Сообщений: 24647
    • Просмотр профиля
Указал бы хоть, о какой платформе речь.
И, думаю, это не в Железо, а в Ромхакинг и программирование

Оффлайн GManiac

  • Пользователь
  • Сообщений: 1284
    • Просмотр профиля
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

  • Пользователь
  • Сообщений: 13
    • Просмотр профиля
Это всё взято из примеров 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

  • Пользователь
  • Сообщений: 1284
    • Просмотр профиля
Почему они так делают - спрашивай у авторов. В случае НЕС можно было бы предположить, что это сделано для облегчения банкования... не знаю, но раз для 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

  • Пользователь
  • Сообщений: 13
    • Просмотр профиля
А ещё раз почему именно 12
а не 8 или 16?

Оффлайн pristavkin

  • Пользователь
  • Сообщений: 13
    • Просмотр профиля

Оффлайн CrazyMax

  • Пользователь
  • Сообщений: 922
  • Пол: Мужской
  • DeSmuME Team
    • Просмотр профиля
^_^
Выучи логику и вопросы самы по себе отпадут  :)