Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

PVS-Studio и 3DO-эмуляторы

Anna | 24.06.2014 | нет комментариев
Клялся огромнее не касаться эмуляции 3DO консоли, раскаиваюсь. Но здесь у меня возникла вероятность поработать с такой экзотической штукой как статический анализатор кода, а именно PVS-Studio. Первое на чем я решил опробовать анализатор безусловно же стал мой эмулятор консоли 3DO (Phoenix Project). В начале 90-х была такая приставка, первая 32-х битная консоль с CD-приводом, помню нам с братом ее папа из Москвы привез, с тех пор никак оторваться не могу. Ну а раз подвернулся случай, то за одно и все основные планы по эмуляции 3DO проверим. Выходит, поехали…

FreeDOCore – подлинное ядро из репозитория в GoogleCode.

Picture 3

Сайт планаhttp://www.freedo.org/.

Ревизия: 8.

Справка: 1-й и дозволено сказать исключительный эмулятор данной консоли, все остальные так либо напротив основаны на данном коде.

Оплошность записи. 

V523: The ‘then’ statement is equivalent to the ‘else’ statement. 

Line 673 — clio.cpp

Picture 12

Такую прелесть обыкновенный компилятор даже за Warning не считает:

if((cregs[0x404])&0x200)
{
  ptr=0;
  while(len>=0)
  {
    b3=_xbus_GetDataFIFO();                
    ...
  }
  cregs[0x400]|=0x80;
}
else
{
  ptr=0;
  while(len>=0)
  {
    b3=_xbus_GetDataFIFO();                
    ...
  }
  cregs[0x400]|=0x80;
}

В данном случае не только ненужный код, но и оплошность эмуляции, протокол XBUS двунаправленный, а в данном случае он работает неизменно на чтение, что безусловно не критично для эмуляции привода компакт-дисков, но все же является дерзкой и допустимо опасной оплошностью для эмулируемых игр – а внезапно какой из них взбредет в голову прожечь болванку?! А если серьезно, то взамен записи в эмулируемый интерфейс XBUS произойдет порча памяти, указанной в DMA регистрах, ну и безусловно с таким подходом никак не получится сэмулировать такой раритет, как FZ-EM256 3DO Memory Unit.

Оплошность чтения. 

V614: Potentially uninitialized variable ‘val’ used. 

Line 803 — clio.cpp

Picture 13

Вначале я подумал, что это галиматья, но внезапно припомнил о привидениях в FIFO…

unsigned short  __fastcall _clio_EIFIFO(unsigned short channel)
{
  unsigned int val,base,mask;
  ...
  if(FIFOI[channel].StartAdr!=0)//channel enabled
  {
    ...
  }
  return val;
}

Тут в ряде случаев допустимо чтение непредсказуемых значений, от того что переменная valинициализируется только при выполнении данные. В теории FIFO DSP процессора позже опустошения должно возвращать последнее считанное из него значение, этакого призрака. И правда на практике чтения из пустого FIFO протекать не обязаны, но кто знает, внезапно позже исправления заведется еще одна игра?

Итого две достойных внимания ошибки, Добросовестно говоря, думал будет огромнее.

FourDO – модифицированное ядро из репозитория на SourceForge

Picture 17

Сайт планаhttp://www.fourdo.com/.

Ревизия: 387.

Справка: Данный план прожил две жизни, первая – когда авторы начали независимо писать эмулятор с нуля, но увы пациент впал в кому, а позже вскрытия начальных кодов FreeDO, началась вторая жизнь, теснее с новыми органами. Выходит, посмотрим, как приживаются имплантаты…

Поправленная, но все еще оплошность.

Picture 4

Сразу хочу подметить последнюю ошибку в подлинном ядре (V614: Potentially uninitialized variable ‘val’ used. Line 803 — clio.cpp), с приведениями там расправились без лишних (либо таки с лишними?) разговоров:

unsigned short  __fastcall _clio_EIFIFO(unsigned short channel)
{
  unsigned int val,base,mask;

  base=0x400 (channel*16);
  mask=1<<channel;

  if(FIFOI[channel].StartAdr!=0)
  {
    ...
  }
  else
  {
    val=0;
    // JMK SEZ: What is this? 
    // It was commented out along with this whole "else" block,
    // but I had to bring this else block back from the dead
    // in order to initialize val appropriately.
  }

  return val;
}

А напрасно расправились! Правдивая задача осталась нерешенной, но со стороны все прекрасно, и о задаче никто бы мог огромнее и не припомнить. Особенно изящным решением было бы объявить переменную val какstatic и инициализировать нулем, а больше верным – перенести за пределы функции и внести в список переменных для стремительных сохранений, ну а блок else – удалить, Дабы не смущал.

Неисправленная оплошность. 

V523: The ‘then’ statement is equivalent to the ‘else’ statement. 

Line 673 — clio.cpp

Picture 19

Тут не ступала нога «Создателя» – все как в оригинале. Для тех, кто не в теме, «Создатель» — это один из авторов FourDO – Виктор (уж не знаю, его это имя либо нет, тот еще Штирлиц ), он же автор 3DOPlay, еще одного форка FreeDO, ошибки в нем те же что и в оригинале. С 3DOPlay была комичная история: Виктор решил потроллить и сказал, что он Создатель Эмулятора 3DO, а разработчики FreeDO якобы украли у него код. К великому моему стыду, я как соавтор FreeDO, не сумел пройти мимо и принимал энергичное участие в боевых действиях вопреки его плана 3DOPlay — чудесное нужно сказать наименование, но кто-то ляпнул — три дупла и понеслась… В результате Виктор перешел в команду FourDO, и нужно отдать должное, он исключительный, кто хоть что-то сделал в плане становления эмуляции 3DO, помимо авторов первоисточника.

Пока еще не оплошность.

V550: An odd precise comparison: Rez2T != 0. It’s probably better to use a comparison with defined precision: fabs(A — B) > Epsilon. 

Line 778 – madam.cpp

Picture 20

Приведенный ниже код всецело работоспособен, но вызывает солидную обеспокоенность.

static double Rez0T,Rez1T,Rez2T,Rez3T;
...
Rez2T=(signed int)((M20*V0 M21*V1 M22*V2)/65536.0);
if(Rez2T!=0) M=Nfrac16/(double)Rez2T;
else M=Nfrac16;

В подлинном плане Rez2T имел тип int, видимо авторы таким образом решили избавиться от предупреждений по реформированию типов, избавились, но если внезапно кто-то решит убрать принудительное приведение к типу signed int – появится риск словить исключение сопроцессора при делении Nfrac16 на Rez2T.

И еще один ломтик кода вызывает у меня солидную обеспокоенность на данный раз здоровьем разработчиков из команды FourDO:

void __fastcall _qrz_PushARMCycles(unsigned int clks)
{
    uint32 arm,cnt;
    int timers=21000000; //default
    int sp=0;
    if(sdf>0) sdf--;
    if(sf>0) sf--;
    if(unknownflag11>0)unknownflag11--;
    if(ARM_CLOCK<0x5F5E10)ARM_CLOCK=0x5F5E10;
    if(ARM_CLOCK>0x2FAF080)ARM_CLOCK=0x2FAF080;
    if(speedfixes>0&&speedfixes<0x186A1) 
      {/*sp=0x2DC6C0;*/ speedfixes--;}
    else if(speedfixes>0x186A1&&speedfixes<0x30D41)
      {/*if(sdf==0)sp=0x4C4B40; */speedfixes--;}
    else if(speedfixes<0) {sp=0x3D0900; speedfixes  ;}
    else if(speedfixes>0x30D41) {/*sp=0x249F00;*/ speedfixes--;}
    else if(speedfixes==0x30D41||speedfixes==0x186A1) speedfixes=0;
    if((fixmode&FIX_BIT_TIMING_2)&&sf<=2500000) 
      {sp=0; timers=21000000; if(sf==0)sp=-(0x1C9C380-ARM_CLOCK);}
    if((fixmode&FIX_BIT_TIMING_1)/*&&jw>0*/&&sf<=1500000)
      {/*jw--;*/timers=1000000;sp=-1000000;}
    if((fixmode&FIX_BIT_TIMING_4)/*&&jw>0*/)
      {/*jw--;*/timers=1000000;sp=-1000000;}
    if((fixmode&FIX_BIT_TIMING_3)&&(sf>0&&sf<=100000)/*&&jw>0*/)
      {/*jw--;*/timers=900000;}
    if((fixmode&FIX_BIT_TIMING_5)&&sf==0/*&&jw>0*/)
      {/*jw--;*/timers=1000000;}
    if((fixmode&FIX_BIT_TIMING_6)/*&&jw>0*/)
      {/*jw--;*/timers=1000000; if(sf<=80000)sp=-23000000;}
    if(fixmode&FIX_BIT_TIMING_7){sp=-3000000; timers=21000000;}
    if((sf>0x186A0&&!(fixmode&FIX_BIT_TIMING_2))||
       ((fixmode&FIX_BIT_TIMING_2)&&sf>2500000))
         sp=-(12200000-ARM_CLOCK);
    if((ARM_CLOCK-sp)<0x2DC6C0)sp=-(0x2DC6C0-ARM_CLOCK);
    if((ARM_CLOCK-sp)!=THE_ARM_CLOCK)
    { THE_ARM_CLOCK=(ARM_CLOCK-sp);
        io_interface(EXT_ARM_SYNC,(void*)THE_ARM_CLOCK); 
        //fix for working with 4do
    }
    arm=(clks<<24)/(ARM_CLOCK-sp);
    qrz_AccARM =arm*(ARM_CLOCK-sp);
    if( (qrz_AccARM>>24) != clks )
    {
        arm  ;
        qrz_AccARM =ARM_CLOCK;
        qrz_AccARM&=0xffffff;
    }
    qrz_AccDSP =arm*SND_CLOCK;
    qrz_AccVDL =arm*(VDL_CLOCK);
    if(_clio_GetTimerDelay())qrz_TCount =arm*((timers)/
      (_clio_GetTimerDelay()));
} 

Приведенный код с точки зрения анализатора верный, но с точки зрения здорового смысла, делать это (что «это» я могу лишь предполагать) при учете тактов эмулируемого процессора – форменное бесчинство, ниже код из оригинала:

void __fastcall _qrz_PushARMCycles(unsigned int clks)
{
 uint32 arm;
        arm=(clks<<24)/ARM_CLOCK;
        qrz_AccARM =arm*ARM_CLOCK;
        if( (qrz_AccARM>>24) != clks )
        {
                arm  ;
                qrz_AccARM =ARM_CLOCK;
                qrz_AccARM&=0xffffff;
        }
        qrz_AccDSP =arm*SND_CLOCK;
        qrz_AccVDL =arm*(VDL_CLOCK);
        if(_clio_GetTimerDelay())
            qrz_TCount =arm*((__temporalfixes?12500000:25000000)/
               (_clio_GetTimerDelay()));
}

В целом дозволено сказать, что пациент ни жив, ни мертв, изменений в ядре эмулятора немного, не все в лучшую сторону, да и нет их теснее больше года, судя по репозиторию.

Phoenix Emu-Project – новому ядру – новые ошибки.

Picture 5

Сайт планаhttp://www.arts-union.ru

Версия: 1.7

Справка: снова написанный эмулятор 3DO, с маниакальной целью доведения эмуляции 3DO до совершенства, правда задумывался мною как мультисистемный эмулятор с соответствующей инфраструктурой кода, но пока только 3DO.

Оплошность – текстуры облезли! 

V501: There are identical sub-expressions to the left and to the right of the ‘!=’ operator: val.flags != val.flags. 

Line 207 — gfx_celengine.h 

Picture 7

struct gfxCelTexturePDECAttrib
{
    uint32 pre0;
    uint32 flags;

    int plutcount;
    uint16 plut[32];

    bool operator==(const gfxCelTexturePDECAttrib &val) const
    {
        if(val.pre0!=pre0)return false;
        if(val.flags!=val.flags)return false;
        if(val.plutcount!=plutcount)return false;
        for(int i=0;i<val.plutcount;i  )
        {
            if(val.plut[i]!=plut[i])return false;
        }
        return true;
    }
};

Оплошность, допущенная по невнимательности и приводящая к текстурным недостаткам в процессе игры, от того что если флаги CEL у текстур в кэше отличаются, а это останется незамеченным, и в остальном они идентичны, то будет использован неверный шейдер для отображения текстуры. Ниже верный вариант:

if(val.flags!=flags)return false;

Оплошность — мусор на экране! 

V579: The memset function receives the pointer and its size as arguments. It is possibly a mistake. Inspect the third argument. 

Line 36 — vdlp_3do.cpp
Picture 22
Здесь все легко – VDLP пал жертвой вновь же невнимательности при модификации связанной с добавлением поддержки игр формата PAL. Прежде эмулятор поддерживал только NTSC игры, которых подавляющее множество, и буфер кадра имел фиксированный размер 320 на 240 пикселей, следственно был объявлен внутри класса в виде массива, без выделений памяти.


screen=new uint8[384*288*3*4];
memset(screen,0,sizeof(screen));

И Дабы оплошность ушла с глаз долой (в буквальном смысле, потому как 1-й, едва уловимый кадр при старте игры заполнен мусором), дозволено написать, скажем, так:


memset(screen,0,sizeof(uint8)*384*288*3*4);

Оплошность – а диска-то нет! 

V595: The ‘adapt’ pointer was utilized before it was verified against nullptr. Check lines: 375, 376.

Line 375 — dumplibrary.cpp
Picture 21
И вновь невнимательность… До обращения к объекту нужно бы проверить его корректность, следственно последние две строчки нужно поменять местами. Напротив при отсутствии необходимых образов получим исключение в процессе загрузки сохраненных игр.


dumpAdapter *adapt=createDumpAdapter(j,
  inf->Parent()->Attribute("attach").toString());
adapt->SetSign(signs[names[i]]);
if(!adapt)break;

Что здесь сказать? Мне нужно быть внимательней либо легко отдыхать по вечерам взамен программирования эмуляторов.

Завершение

Выходит, мой 1-й навык показал, что статический обзор кода, вещь бесспорно пригодная и способная сэкономить много времени и нервов разработчиков. Для эму-сцены цена вопроса безусловно высока, как и в случае с декомпилятором Hex-Ray для ARM, тот, что мог бы продвинуть эмуляцию 3DO на много шагов вперед, но в различии от Hex-Ray, у PVS-Studio возник бюджетный аналог – CppCat (http://www.cppcat.com), тот, что не так резок, но дело свое знает.

 

Источник: programmingmaster.ru

 

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB