Vlákno názorů k článku Hrátky s barvovou paletou a vykreslení jednotlivých pixelů kartou CGA od CHe - Bol som zvedavý, jednak ako bude mať tá...

  • Článek je starý, nové názory již nelze přidávat.
  • 14. 7. 2024 9:52

    CHe

    Bol som zvedavý, jednak ako bude mať tá ručne vyčačkaná ASM frame copy implementácia vo vzťahu k naivným C-čkovým a dvak, ako to pobeži na reálnom hw.:
    https://pastebin.com/6fu6Bi7D

    Prekladal som to prekladačom z TC++ 3.0 v DOSBoxe, pre porovnanie je tam inlajnutá aj tá Pavlova implementácia.

    Na najstaršom funkčnom PC čo mám momentálne po ruke (Amstrad PPC 640, NEC V30 @ 8Mhz, 1987) to vyzerá takto:

    C assign b far:   3.4 fps, 0.2 Mpxps
    C assign w far:   5.2 fps, 0.3 Mpxps
    C assign l far:   5.7 fps, 0.4 Mpxps
    C assign w near:  5.5 fps, 0.3 Mpxps
    C assign l near:  6.4 fps, 0.4 Mpxps
    C fmemcpy:       19.2 fps, 1.2 Mpxps
    ASM movsw:       22.3 fps, 1.4 Mpxps

    (fmemcpy je interne implementovaná tiež pomocou rep movsw, len teda šaškovanie so zásobníkom pri jej volaní zožerie nejaké cykly na každý riadok)

    V30 je upgradnutá 8086, ak by to niekto chcel testnúť na niečom ešte bližšom pôvodnému XT s 8088 @ 4,77MHz, môžem posunúť binárku.

  • 14. 7. 2024 20:58

    Pavel Tišnovský
    Zlatý podporovatel

    Díky za doplnění!

    Jak vlastně vypadá výstup v assembleru z toho TC++? Podle benchmarků to nebude nic moc (to mě překvapuje popravdě). Nepomůžou nějaký triky typu modifikátor "registry", přepis smyček tak, aby se explicitně pracovalo přes pointry (a ne jakoby šlo o pole) atd.?

    (je asi blbé, že se ptám. TC jsem hodně používal, učil jsem se na tom céčko, ale už ho nemám)

  • 14. 7. 2024 22:17

    CHe

    divoko, vútorné x-ové slučky vyzerajú takto:

    frame_copy_movb()

    0000351D  8B1EB006          mov bx,[0x6b0]
    00003521  8A00              mov al,[bx+si]
    00003523  C41EAB00          les bx,[0xab]
    00003527  268801            mov [es:bx+di],al
    0000352A  46                inc si
    0000352B  47                inc di
    0000352C  FE46FF            inc byte [bp-0x1]
    0000352F  807EFF50          cmp byte [bp-0x1],0x50
    00003533  72E8              jc 0x351d

    frame_copy_mov­w_near()

    0000367D  8BC6              mov ax,si
    0000367F  D1E0              shl ax,1
    00003681  8B1EB006          mov bx,[0x6b0]
    00003685  03D8              add bx,ax
    00003687  8B07              mov ax,[bx]
    00003689  8BD7              mov dx,di
    0000368B  D1E2              shl dx,1
    0000368D  8B1EAB00          mov bx,[0xab]
    00003691  03DA              add bx,dx
    00003693  268907            mov [es:bx],ax
    00003696  46                inc si
    00003697  47                inc di
    00003698  FE46FF            inc byte [bp-0x1]
    0000369B  807EFF28          cmp byte [bp-0x1],0x28
    0000369F  72DC              jc 0x367d

    frame_copy_mo­vl_near() je už úplná divočina s 25 inštr. a plejádou push/pop v každej it. (aj tak je ale o chlp rýchlejší než ten movw, keďže presunie 32b na it.)

    a hej, keď sa array indexovanie nahradí čisto ptr aritmetikou:

    void frame_copy_movw_near_ptr() {
      unsigned char x, y, b;
      unsigned int _es *screen_p;
      unsigned int *buf_p;
    
      _ES = FP_SEG(screen);
      for (b = 0; b < SCR_BANKS; b++) {
        screen_p = &screen[b * SCR_BANK_OFFSET];
        buf_p = &buf[b * SCR_LINE_OFFSET];
        for (y = 0; y < SCR_H / SCR_BANKS; y++) {
          for (x = 0; x < SCR_LINE_OFFSET / 2; x++) {
            *screen_p++ = *buf_p++;
          }
          buf_p += SCR_LINE_OFFSET / 2;  // skip even/odd line of src
        }
      }
    }

    vedie to k relatívne decentnému výsledku:

    000036FC  8B04              mov ax,[si]
    000036FE  268905            mov [es:di],ax
    00003701  83C602            add si,byte +0x2
    00003704  83C702            add di,byte +0x2
    00003707  FE46FF            inc byte [bp-0x1]
    0000370A  807EFF28          cmp byte [bp-0x1],0x28
    0000370E  72EC              jc 0x36fc

    škoda akurát tej loop premennej v stack frame namiesto registra a add si/di, 8b neviem či je optimálnejšie než 2x inc

    Nie som teraz pri tom Amstrade, no v DOSBoxe je toto zhruba 1,7-krát efektívnejšie než najrýchlejší z tých array index prístupov.

  • 14. 7. 2024 23:14

    CHe

    pre tú kritickú loop premennú sa dá vynútiť register explicitne:

    for (_CL = SCR_LINE_OFFSET / 2; _CL > 0; _CL--) {
      *screen_p++ = *buf_p++;
    }

    potom je to:

    000036FA  8B04              mov ax,[si]
    000036FC  268905            mov [es:di],ax
    000036FF  83C602            add si,byte +0x2
    00003702  83C702            add di,byte +0x2
    00003705  FEC9              dec cl
    00003707  0AC9              or cl,cl
    00003709  77EF              ja 0x36fa

    a aj toho OR CL, CL testu by sa asi dalo zbaviť prepisom na while{} alebo do{}while. Tu robí hneď na úvod JMP na ten OR test, aby otestoval, či náhodou už úvodné SCR_LINE_OFFSET / 2 nie je rovné 0 (hoci je to v tomto prípade nenulová konštanta). To už potom ale človek ohýba to C-čko takmer na assembler... :D