1 Ostatnio edytowany przez ilmenit (2009-03-12 11:20:57)

Hej,

Zacząłem optymalizować krytyczny pod względem szybkości fragment kodu w mojej grze, ponieważ CC65 generuje kod wyjątkowo brzydki.
Pytanie - w jaki sposób najszybciej można wykonać poniższą operację? ASMa 6502 w miarę znam, ale nie znam sztuczek optymalizacyjnych przy pisaniu w nim...
Chodzi o równoważnik asemblerowy kodu w C: 

if (Map[x+y*MAP_SIZE_X]==TILE_WALL)
      shadow=TRUE;

MAP_SIZE_X mogę mieć dowolne, np. 32 gdyby przesunięcia bitowe były w stanie kod przyspieszyć.
TILE_WALL też mogę mieć dowolne, ale najlepiej, gdyby było to 0 lub 1.
Ponieważ MAP_SIZE_Y też mogę wybrać (aktualnie 32), może warto przeliczyć wskaźniki dla poszczególnych Map+y*MAP_SIZE_X i wrzucić je na stronę zerową?

      00041Ar 1               ; if (Map[x+y*MAP_SIZE_X]==TILE_WALL)
      00041Ar 1               ;
      00041Ar 1  A0 04        L00BE:    ldy     #$04
      00041Cr 1  A2 00            ldx     #$00
      00041Er 1  B1 rr            lda     (sp),y
      000420r 1  20 rr rr         jsr     pushax
      000423r 1  A0 05            ldy     #$05
      000425r 1  B1 rr            lda     (sp),y
      000427r 1  20 rr rr         jsr     pushax
      00042Ar 1  A9 05            lda     #$05
      00042Cr 1  20 rr rr         jsr     tosshlax
      00042Fr 1  20 rr rr         jsr     tosaddax
      000432r 1  18               clc
      000433r 1  69 rr            adc     #<(_Map)
      000435r 1  85 rr            sta     ptr1
      000437r 1  8A               txa
      000438r 1  69 rr            adc     #>(_Map)
      00043Ar 1  85 rr            sta     ptr1+1
      00043Cr 1  A0 00            ldy     #$00
      00043Er 1  B1 rr            lda     (ptr1),y
      000440r 1  C9 01            cmp     #$01
      000442r 1  D0 02            bne     L00C3
      000444r 1               ;
      000444r 1               ; shadow=TRUE;
      000444r 1               ;
      000444r 1  91 rr            sta     (sp),y

Źródła tosshlax z CC65 są tu:
http://svn.xp-dev.com/svn/gideonz_1541U … time/shl.s

2 Ostatnio edytowany przez mono (2009-03-12 13:11:15)

if (Map[x+y*MAP_SIZE_X]==TILE_WALL)
      shadow=TRUE;
.A=y, .Y=x, .X=TILEWALL=0|1, MAPSIZE=32, shadow=0|1, adr=2 bajty zpg

  lsr @
  ror adr
  lsr @
  ror adr
  lsr @
  ror adr
  pha
  lda adr
  and #%11100000
  clc
  adc #<Map
  sta adr
  pla
  adc #>Map
  sta adr+1
  txa
  eor (adr),y
  bne exit
  lda #1
  sta shadow
exit equ *

gdy stablicujesz sobie y wtedy:
.X=y, .Y=x, .A=TILEWALL=0|1, MAPSIZE=32, shadow=0|1, adr=2 bajty zpg

  pha
  lda ltab,x
  sta adr
  lda htab,x
  sta adr+1
  pla
  eor (adr),y
  bne exit
  lda #1
  sta shadow
exit equ *

jeśli zaś tablica mogłaby być na stronie zerowej cała w fromacie lsb, msb, lsb, msb... wtedy:
.A=y=[0..127], .Y=x, .X=TILEWALL=0|1, MAPSIZE=32, shadow=0|1

  
  asl @
  adc #Tab
  sta code+1
  txa
code equ *
  eor (adr),y
  bne exit
  lda #1
  sta shadow
exit equ *

jeśli TILEWALL ma być stałe to:
.A=y=[0..127], .Y=x, TILEWALL=1, MAPSIZE=32, shadow=0|1

  
  asl @
  adc #Tab
  sta code+1
code equ *
  lda (adr),y
  beq exit
  sta shadow
exit equ *

pytanie czy da się swobodnie manipulować przypisaniem zmiennych do rejestrów i umieścić tablicę na stronie zerowej?

hex, code and ror'n'rol
niewiedza buduje, wiedza rujnuje

3 Ostatnio edytowany przez marok (2009-03-12 14:02:15)

@mono: nie ma problemu przy wyjściu z procki przez exit z wartością 0 w shadow?

@ilmenit: poniżej jest wersja altermatywna (być może nieco szybsza) procki do wersji zaproponowanej przez mono

 lda #0
 sta ahY
 lda MAP_SIZE_Y
 asl @
 asl @
 asl @
 asl @        ; 16 * MAX_SIZE_Y max. % 1 1111:0000
 rol ahY
 asl @        ; 32 * MAX_SIZE_Y max. %11 1110:0000
 rol ahY
 adc MAP_SiZE_X
 adc <MAP
 tax        [sta adrMAP]

 lda ahY
 adc >MAP
 sta adrMAP+1

adrMAP equ *+1
 lda MAP,x    [lda MAP]
 cmp #1
 rts        [zn. C=0 -> shadow==true, c=1 * == false (dla TILEWALL=0)]

4 Ostatnio edytowany przez mono (2009-03-12 14:21:28)

@marok: rzeczywiście - wyszedłem z założenia, że 3 przesunięcia będą szybsze, a nie policzyłem :)
Problemu chyba nie ma bo realizujemy funkcję if cond shadow=true a więc nie wiadomo co ma być w shadow wpp. Jeśli wiadomo, to Twoja procedura jest lepsza.

hex, code and ror'n'rol
niewiedza buduje, wiedza rujnuje

5

Klasyczną metodą jest też zrobienie map 256-bajtowej szerokości - jeśli nie używasz całej strony, może da się użyć pozostałej części do czegoś innego.
Wszystko zależy jak bardzo chcesz to zoptymalizować.

: 404. Stopka not found

6

eru napisał/a:

Klasyczną metodą jest też zrobienie map 256-bajtowej szerokości - jeśli nie używasz całej strony, może da się użyć pozostałej części do czegoś innego.
Wszystko zależy jak bardzo chcesz to zoptymalizować.

256 bajtów szerokości to ciut za dużo :-)
Dzięki za pomysły, są w zupełności wystarczające.

7 Ostatnio edytowany przez jellonek (2009-03-12 15:09:40)

jesli mapa wyrownana jest do adresu strony to a jej szerokosc to 32 bajty to:
adres_hi = numer_strony_mapy + (y >> 5)
adresl_lo = (y << 3) & 0xff + x

y >> 5 to 5x lsr @
(y << 3) & 0xff to 3x asl @

The UNIX Guru`s view of Sex:
unzip; strip; touch; finger; mount; fsck; more; yes; umount; sleep

8 Ostatnio edytowany przez marok (2009-04-04 20:26:51)

@ilmenit: nie obyło się bez błędu w prodce

instrukcja ADC <MAP jest niepotrzebna i jeśli adres mapki nie zaczyna się od $xx00, to skutkuje źle wyliczonym adresem

w ramach skracania zbędnego kodu dadam jeszcze, że jeśli adres MAP dzieli się w całości przez $400, to można pozbyć się kolejnych kilku istrukcji (wersja po zmianiach):

 lda >MAP/4
 sta adrMAP+2
 lda MAP_SIZE_Y
 asl @
 asl @
 asl @
 asl @
 rol adrMAP+2
 asl @
 rol adrMAP+2
 adc MAP_SiZE_X
 tax

adrMAP equ *+1
 lda MAP,x
 cmp #1
 rts

EDIT: przypadkowo zaglądając dziś do kopii tego kawałka kodu zauważyłem istotny błąd który chcę skorygować

w rozkazach rol adrMAP+2 w obu przypadkach i sta adrMAP 2-kę należy zastąpić 1-ką (*+1).