1 Ostatnio edytowany przez pr0be (2006-03-28 15:22:53)

potrzebuje szybkiej procedury do mnozenia liczb 16bitowych ze znakiem razy liczbe 8bitowa ze znakiem.
Aktualnie moja procka wyglada tak i korzysta z zaleznosci ,ze:
A*B = B*(A_LO + 256*A_HI) = A_LO*B + 256*A_HI*B

; 16bit signed by 8bit signed multiplication
;
; input: a=:1, b=:2,:3
; return: a*b=:4 (24bit result)
;
smul16 .macro ' '
    lda :1
    bpl pos
    eor #$ff
    clc
    adc #1
pos    sta sqr1l
    sta sqr1h
    eor #$80
    sta ssqr1l
    sta ssqr1h
    eor #$ff
    sta ssqr2l
    sta ssqr2h
    eor #$80
    sta sqr2l
    sta sqr2h

    sec
    lda :3 ; hi
    eor #$80
    tay
    lda (ssqr1l),y
    sbc (ssqr2l),y
    sta :4+1
    lda (ssqr1h),y
    sbc (ssqr2h),y
    sta :4+2
    sec
    ldy :2 ; lo
    lda (sqr1l),y
    sbc (sqr2l),y
    sta :4
    lda (sqr1h),y
    sbc (sqr2h),y

    clc
    adc :4+1
    bcc t0
    inc :4+2
t0    sta :4+1
    lda :1
    bpl endmul
; fix sign
    lda :4
    eor #$ff
    clc
    adc #1
    sta :4
    lda :4+1
    eor #$ff
    adc #0
    sta :4+1
    lda :4+2
    eor #$ff
    adc #0
    sta :4+2
endmul
.endm

sqrl1 to tablica funkcji f(x) = (x*x)/4 gdzie x jest liczba bez znaku
ssqrl2 to tablica funkcji f(x) = (x*x)/4 gdzie x jest liczba ze znakiem
(dla przypomnienia: a*b = f(a+b) - f(a-b) )

takie cos zajmuje srednio ~130cykli, w najgorszym wypadku 156...
problem polega na tym ,ze tracimy ~40cykli na zmiane znaku wyniku (i mnoznika)
czy da sie jakos sprytnie skonstruowac tablice, tak aby nie tracic czasu na te operacje?
gdzies kiedys czytalem na jakims forum c64 ,ze da sie pomnozyc 16bitsigned by 8bit signed w <100cykli, mysle ,ze ich procedura byla podobna tylko miala lepsze tablice, tak ,ze nie trzeba bylo odwracac wyniku, dzieki czemu mogli to zrobic w <100cykli...
pytanie tylko jak takie tablice maja wygladac...

2 Ostatnio edytowany przez eru (2006-03-28 01:04:37)

Nie do konca lapie jak to dziala, musialbym sobie rozpisac te tablice, ale szybkie sugestie to:
- stworzyc 2 wersje, jedna dla dodatnich, druga dla ujemnych, zeby sprawdzac znak a tylko raz
- Twoja metoda odwracania znaku liczby (lda liczba, eor #$ff, (clc ,) adc #(0/1), sta liczba) da sie zastapic szybsza: lda #0, (sec ,) sbc liczba, sta liczba. Oszczedzasz 3*2=6 cykli.
Ale to malutkie wszystko, tak z marszu nic wiecej nie widze.
Przy okazji - zakladam, ze a jest za kazdym razem inne ofkorz?
Aha, i ten wzorek nad kodem cos nie tego :D

Aha, jeszcze jedno... moze da sie oszczedzic i uzyc tych samych komorek do mnozenia hi i lo bajtu B, gdybys normalizowal A i B zeby byly dodatnie oba, sprawdzil znak (A XOR Bhi) i mial 2 wersje w zaleznosci od tego znaku (produkujaca dodatni lub ujemny wynik). Moze by cos pomoglo, nie wiem, ide spac :P

: 404. Stopka not found

3

Aha, jeszcze jedno... moze da sie oszczedzic i uzyc tych samych komorek do mnozenia hi i lo bajtu B, gdybys normalizowal A i B zeby byly dodatnie oba, sprawdzil znak (A XOR Bhi) i mial 2 wersje w zaleznosci od tego znaku (produkujaca dodatni lub ujemny wynik

oczywiscie da sie i dzieki czemu zaoszczedziemy ~16cykli ale wtedy w razie konieczonosci trzeba by bylo dodatkowo odwracac A a to strata 24cykli na odwrucenie i 5/6 cykli na sprawdzenie znaku (w aktualnej wersji nie trzeba A odwracac), wiedz to by bylo szybsze jesli A>=0, a jesli zalozyc ,ze rozklad liczb dodatnich i ujemnych bedzie taki sam to raczej na tym stracimy... (poniewaz dla ujemnych stracimy 13-14cykli a na dodatnich zyskamy tylko 11-12cykli w stosunku do wersji ktorej aktualnie uzywam)


Aha, i ten wzorek nad kodem cos nie tego

oczywiscie zamiast: A*B = B*(A_LO + 256*B_HI) = A_LO*B + 256*A_HI*B, powinno byc: A*B = B*(A_LO + 256*A_HI) = A_LO*B + 256*A_HI*B (B - liczba 8 bitowa, A - liczba 16bitowa)

stworzyc 2 wersje, jedna dla dodatnich, druga dla ujemnych, zeby sprawdzac znak a tylko raz

dobry pomysl, na pierwszy rzut oka dajacy 2 cykle zysku dla dodatnich i 5 cykli dla ujemnych (albo 5cykli zysku dla dodatnich i 2 cykle zysku dla ujemnych - w zaleznosci od tego w jakiej kolejnosci umiesicmy podprocedury)

- Twoja metoda odwracania znaku liczby (lda liczba, eor #$ff, (clc ,) adc #(0/1), sta liczba) da sie zastapic szybsza: lda #0, (sec ,) sbc liczba, sta liczba. Oszczedzasz 3*2=6 cykli.

huhu faktycznie 6cykli zysku, to juz cos! dzieki wielkie! ;)

4 Ostatnio edytowany przez swiety (2006-03-28 19:11:28)

Jeśli używasz makro to czemu stosujesz adresowanie lda (zp),y - chba mniej eleganckim choć szybszym sposobem byłoby zastosowanie kodu samomodyfikującego się np.

sta tu1 ; ssqrl1


lda $8000,y
tu1 equ *-2

zyskasz 1 cykl na kazdej takiej operacji

Po za tym nie do końca rozumiem offsetu eor #$80 - może udałoby się zastosować inne tablice aby wyrzucić toakie rozwiązanie , po połączeniu z samomodyfikacją procedurka powinna dostać większego kopa - cały czas zastanawiam się nad tymi tablicami - jak możesz to podeślij mi jakiś sensowny przykład na maila - koniecznie z tablicami to spróbuję coś pokombinować - myślę że chyba wiem jak temu zaradzić, choć jedynym sposobem będzie to co pisał Eru - zastosować mnożenie dodatnie i osobno ujemne - tutaj przy rozpisaniu tablic można zrobić wypieprzenie tego eor #$ff ,clc , adc #1 - zmieniając kolejność w tablicach przy ujemnych liczbach!

5 Ostatnio edytowany przez eru (2006-03-28 20:01:34)

Niestety, Swiety, zmiana z zerowej na absolutny nie przyspieszy, bo co prawda lda $8000,y jest 1 cykl szybsze (4 vs 5) to sta tu1 jest 1 cykl wolniejsze (4 vs 3).
Pr0be, jakbys mial przyklad z tablicami (wiem wiem, Fox kiedys to gdzies opublikowal), podeslij no na priva albo wystaw na www i zapodaj sznurek.
Swiety, gratz za $100 posta :)

: 404. Stopka not found

6

Jeśli używasz makro to czemu stosujesz adresowanie lda (zp),y - chba mniej eleganckim choć szybszym sposobem byłoby zastosowanie kodu samomodyfikującego się np.

sta tu1 ; ssqrl1


lda $8000,y
tu1 equ *-2

zyskasz 1 cykl na kazdej takiej operacji

troche zle policzyles cykle, bo zysk bedzie zerowy "sta adres" zjada 4cykle a nie 3(chyba ,ze procka by byla na stronie zerowej co raczej odpada bo trzeba by bylo korzystac z jsr/rts co zjada 12cykli ;>), "lda $8000,y" jest o jeden cykl szybsza niz "lda ($00),y" ale ten 1 cykl zysku traci sie po przez "sta adres16" zamiast "sta adres8"

z usunieciem eor #$80 / eor #$ff i zrobieniem dodatkowych tablic to dobry pomysl ;)
ale z "lda :1 eor #$80 tay" chyba juz nie bedzie tak latwo, aby to zastapic samym "ldy :1"

zastosować mnożenie dodatnie i osobno ujemne - tutaj przy rozpisaniu tablic można zrobić wypieprzenie tego eor #$ff ,clc , adc #1 - zmieniając kolejność w tablicach przy ujemnych liczbach!

no tak tylko ,ze uwzgledniajac tyle mozliowsci te tablice beda juz zajmowac ~32k, zmiana bankow zajmuje tez troche cykli, wiedz moze byc to nie warte zachodzu...

dzieki za odpowiedz, zachwilke wysle przyklad na mail'a

7 Ostatnio edytowany przez swiety (2006-03-28 20:32:15)

Fakt - pomyliłem ilośc cykli - myślałem że lda (zp),y zajmuje 6 a przecież nie 6 tylko 5 !

8 Ostatnio edytowany przez pr0be (2006-03-28 20:37:31)

http://manta.univ.gda.pl/~pwisnie/math.zip - tutaj jest przyklad.

Dodam jeszcze ,ze tablice to funkcje f(x) = (x*x)/4, a format liczb znak/modul jest inny niz standardowy! tzn liczba $81 to jest $1, $92 to $12, $01 = -2 (dlatego robimy operacje eor #$80), jest to pomysl FOX'a, dzieki takiej reprezentacji nie bedziemy mieli przepelnienia mnozac liczbe np. 128*63, bez eor #$80 i uzywajac zwyklych tablicach tzn ,takich ze f(81) = (-127*-127)/4, jesli (a+b)>127 lub (a+b)<-128 to otrzymamy bledny wynik...

9

eru napisał/a:

metoda odwracania znaku liczby (lda liczba, eor #$ff, (clc ,) adc #(0/1), sta liczba) da sie zastapic szybsza: lda #0, (sec ,) sbc liczba, sta liczba. Oszczedzasz 3*2=6 cykli.

Tylko 2:

metoda 1: lda liczba 4c., eor #$ff 2c., clc 2c., adc #1 2c., sta liczba 4c. - suma 14 cykli.
metoda 2: lda #0 2c., sec 2c., sbc liczba 4c., sta liczba 4c. . - suma 12 cykli.

chyba, że w kontekście wielokrotnego użycia to 3*2 było :)

jak sie chce miec C na pewno wyzerowane po tej operacji do nastepnej (np. do adc :P)
choć tu się to raczej nie przyda, a nie chce sie dodatkowego sec w srodku to jeszcze mozna np.
clc, lda liczba, bpl next, lda #$01, sbc liczba, sta liczba, next: ... i w tym miejscu C jest 0
działa to oczywiście też z liczbami kilkubajtowymi,  jako że skoro bpl nie zachodzi
to po odjęciu najstarszego bajtu na pewno C bedzie wyzerowane ;P np.

clc lda int4+3, bpl dodatnia,
lda #$01, sbc int4 sta int4
lda #$00 sbc int4+1...
lda #$00, sbc int4+3, sta int4+3
dodatnia: ... C=0

---==<<Sc0rpi0>>==---

10

A mnie ciekawi dlaczego wymagana jest taka dokladnosc, ze wynik musi byc 24-bitowy. Dlaczegoby nie olac najmlodszych 8 bitow wyniku?

11

Sc0rpi0 napisał/a:

clc lda int4+3, bpl dodatnia,
lda #$01, sbc int4 sta int4
lda #$00 sbc int4+1...
lda #$00, sbc int4+3, sta int4+3
dodatnia: ... C=0

no jesli zalozyc ,ze zawsze po operacji mnozenia bedzie sie dodawac to zyskamy 2 cykle, jesli B jest ujemna, zawsze cos ;)

laoo/ng napisał/a:

A mnie ciekawi dlaczego wymagana jest taka dokladnosc, ze wynik musi byc 24-bitowy. Dlaczegoby nie olac najmlodszych 8 bitow wyniku?

oczywiscie mnozenie bedzie uzywane do obrotow punktow:

X'=(M1*X + M2*Y + M3*Z) / 128

dzielnie liczby 24bitowej przez 128 to tylko 15cykli (niezaleznie od tego czy jest ujemna czy nie! - przy 16bitowej liczbie trzeba by bylo to sprawdzac...), jesli by olac ostatnie 8bitow to bysmy mieli dzielnie przez 256 i mniejsza dokladnosc (w wysokiej rozdzielczosci to tak nie przeszkadza ale w rozdzilczosci powiedzmy 80x48 cos takiego jest bardzo widoczne bo obiekt przy obrocie trzesie sie jak galareta)

12 Ostatnio edytowany przez Sc0rpi0 (2006-03-29 10:08:46)

A co do problemu wczesniejszego (czyli z samymi tablicami i mnozeniem) to
faktycznie chyba powinno sie dac po prostu wsadzic 2x tyle w tablicach
i uwzgledniac znaki obu liczb (a i b). Wtedy juz nie powinno byc potrzeby
korekcji wyniku po wyliczeniu z tablic i dodaniu co troche by scielo czas wykonania.
Zastanowie sie jak sie wyspie bo obecnie myslenie mi sie juz wylacza :)

Eeee...eeedit :P
Wlasnie tak sobie wykombinowalem hmm jakby zmieniac w locie opcode sbc na adc...
Ale to taka na razie mglista idea bo naprawde nie mysle juz :). pokombinuje jak wstane,
ale pewnie sie okaze, że niewykonalne a ja juz mam majaki senne... :P

---==<<Sc0rpi0>>==---

13 Ostatnio edytowany przez jellonek (2006-03-29 11:51:05)

tak niesmialo spytam czy http://www.6502.org/source/integers/fastmult.htm ma sie jakos do tego topicu?
to tylko takie niesmiale pytanie...

lub to: http://www.strotmann.de/twiki/bin/view/ … iplication

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

14

Pierwszy link ofkorz uzywa podobnej metody do mnozenia 8x8, tez z tablicami x*x/4, to stary pomysl.\
Drugi link to 'mnozenie glupie', STRASZNIE wolne.
Ale jak zoptymalizowac 16x8 to jeszcze nie widzialem. W Numenie jest bodajze 16x16, ale nie zoptymalizowane.

: 404. Stopka not found

15

Nie jest znów tak wolne :) , to tylko kwestia procka niestety...
6502 jest fajny ale ma tylko jeden rejestr do szerszych zastosowan
czyli A :). Ciekawe jakby to wygladalo gdyby byly takie opcodes
jak XSL XOR (nie mylic z EOR :P) XSR XOL YSL YOR YSR YOL :)
o dodawaniu czy odejmowaniu rejestr-rejestr to nie mowie, bo
to juz smierdzi Intelem :) a poza tym takie YSR chyba za cięzkie
do zrobienia by nie bylo - a wlasnie przesuniecia, INC i DEC
to oprocz instrukcji sterujacych jak BRK JMP czy RTI/S chyba
najbardziej cyklozerne jezeli wykonuja sie na pamieci.
Oczywiscie stablicowane jest szybsze, ale zzera jednak te
pare stron pamieci jak by nie patrzec.

---==<<Sc0rpi0>>==---

16 Ostatnio edytowany przez laoo/ng (2006-03-29 19:45:50)

Fakt, ze drugi akumulator z mozliwoscia operacji logicznych/arytmetycznych pomiedzy dwoma akumulatorami bylby fajny. Wszystko w dwoch cyklach ;) Ale niestety nikt o tym nie pomyslal. Nawet troche dziwne rozszerzenie 65c02 w postaci 65ce02 mialo trzeci rejestr indeksujacy Z (tak jakby X i Y bylo za malo), a drugiego akumulatora nie bylo. Coprawda 65c816 ma gorna polowke 16-to bitowego C czyli B, ale jedyna operacja na nim to zamiana polowkami z A (XBA).

17

scorp: zapomniales o instrukcji "LOL"
duuze niedopatrzenie :D

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

18

Nie offtopikować!
I nie dyskutować z wujkiem eru :) Jak wujek eru mówi, że wolne, to wolne - przecież tam jest pętla co się wykonuje 16 razy! Na jakim procku byś nie robił, to jest po prostu wolny (czytaj głupi) algorytm.
pr0be, sorki, nie spojrzałem jeszcze w kod, ale cierpię na chroniczny brak czasu :(
Ale ogólnie fajnie, że taki topic się pojawił - wspólnymi siłami wyciśniemy z tego kodu co się da :)
A potem koniecznie do Atariki.

: 404. Stopka not found

19

eru napisał/a:

Na jakim procku byś nie robił, to jest po prostu wolny (czytaj głupi) algorytm.

Bo też chyba on nie ma być jakiś specjalnie szybki - za to kod zajmuje z 50 bajtów, a nie pół pamięci.

KMK
? HEX$(6670358)

20

A pytanie do pr0be: ile pamieci chcesz/mozesz przeznaczyc na tablice? Bo na przyklad w TK mielismy potablicowane mnozenia/dzielenia na jakies 4 banki pamieci. Mozna pokombinowac tez w te strone.

21 Ostatnio edytowany przez Sc0rpi0 (2006-03-31 18:55:25)

drac030 napisał/a:
eru napisał/a:

Na jakim procku byś nie robił, to jest po prostu wolny (czytaj głupi) algorytm.

Bo też chyba on nie ma być jakiś specjalnie szybki - za to kod zajmuje z 50 bajtów, a nie pół pamięci.

Tia, ma byc maly i nie zzrerac na tablice... A BTW dla jaj sobie zrobilem cos takiego:  :P

v8     equ adres8    ; co
v16     equ adres16    ; przez co
v24     equ adres24    ;wynik

        org ~$00fd+t24-fix1 ; !!! * zob. komentarz * !!!
t24     dta b(0)
t8      dta b(0)
        inc t24
loop    asl t8
        beq fin
        bcc rolo
        asl fix0-1
        rol fix1-1
        rol t24
        bcc loop
rolo    asl fix0-1
        rol fix1-1
        rol t24
        lda #$00
fix0    adc #$00
        sta fix0-1
        lda #$00
fix1    adc #$00
        sta fix1-1
        bcs loop-2
        jmp loop
fin     ldy fix0-1
        ldx t24
        sty v24
        sta v24+1
        stx v24+2
        rts

        org mult8x16
        lda #$00
        sta t24
        sta fix0-1
        sta fix1-1
        sec
        ldy v8
        bmi mi8
        lda v16
        sta fix0+1
        lda v16+1
        sta fix1+1
        tya
        eor #$ff
        sta t8
        bne pl8
mi8     sbc v16
        sta fix0+1
        lda #$00
        sbc v16+1
        sta fix1+1
        dey
        sty t8
        sec
pl8     rol t8
        bcs *+5
        jmp rolo
        jmp loop

; org ~$00fd+t24-fix1 - zapis symboliczny ;) pewnie etykiet nie
;                       uzna z wyprzedzeniem zwykly asembler.
; W skrócie trzeba dac taki adres zeby w sekwencji fix1 ... jmp loop
; jmp ladowalo pod $00ff

Czy optymalnie mniej wiecej jest to mi sie nie chcialo sprawdzac :),
ale tak pi x oko jak patrzylem to powinno byc przy najgorszych
zalozeniach jakies 2-2,5 raza gorsze niz to pr0ba na tablicach
(z tego co mowil najgorsza mozliwosc to jakies 150 cykli).
Przy lepszych nie wiem ;) ale spada mu sporo cykli na kazde 0
w miejscie 1 w mnozniku :). No i ma te ;P wade ze troche zerowki
plus stosu zzera, ale jak mowilem nudzilo mi sie :P.

(EDIT) Buuu :) cykl straty hehe - nie jmp ale fin powinno byc pod $ff
bo bedzie +1 przy skoku. :P ...

---==<<Sc0rpi0>>==---

22 Ostatnio edytowany przez eru (2006-04-02 11:43:41)

Właśnie gadałem z Foxem, wyszło nam, że da się toto zrobić w 88-96 cykli, przy użyciu 4K tablic.
Niestety, musieliśmy użyć pewnej technologii, której się nauczyliśmy w Microsofcie, i chyba bez zgody Billa albo Steve'a nie możemy tego opublikować...

prima aprilis! :P

: 404. Stopka not found

23

Iiiitam, od razu techniki Microsoftu :) ...
Z 4k tablicami to podejrzewam na nybblach sie zrobi spokojnie,
choć znów mówie to tak tylko pi x oko (bo moze i nie :P), ale
jeżeli dzisiaj mi się znów nudzić będzie to a nuż coś wymodzę
na Prima Aprilis :P.

---==<<Sc0rpi0>>==---

24

laoo->co do ilosci pamieci na tablice, to im mniej tym lepiej, mysle ,ze rozsadna wielkosc to max 16k, tablicowanie wszystkich mozliwych wynikow:
unsigned*signed: 256*256*WORD=128k, unsigned*unsigned: 256*256*WORD=128k, razem=256k hehehehehe
oczywiscie tablice unsigned*signed mozna olac i korzystac z tablicy unsigned*unsigned (ale wtedy trzeba by bylo zmieniac znak jak bedziemy mnozyc unsigned*signed), 256k na tablice mnozenia to "lekki" HARDCORE =0), pozatym watpie zeby to bylo duzo szybsze... (tablica 32k(128*128*WORD) nie starcza aby pomnozyc 16bit*8bit bo mlodszy bajt trzeba traktowac jako UNSIGNED a nie signed, min to tablica 64k(256*128*WORD), ale przy takiej tablicy mysle ,ze szybkosc bedzie porownywalna z wersja na tablicach kwadratow, poniewaz trzeba w niektorych wypadkach odwracac zarowno A jak i B)

eru->zapytac sie Fox'a to dobra idea, on ma w tej dziedzinie chyba najwieksze doswiadczenie ;)

Sc0rpi0->szczerze to nie wiem jak to dziala, ale jesli jest 2-2,5 razy wolniejsza to w moim przypadku sie raczej nie przyda ;)

25

Spokojnie, Pr0be :) to bylo tylko tak, dla jaj, ze da na shiftach mozna
znow nie tak wolna procke zrobic - w tej chwili kombinuje z inna procka
z tablicami do jakichs 4k. Jak wyjdzie to moze bedzie nieco szybsze.
Ciężko mi powiedzieć w tej chwili co z tego bedzie bo w zamyśle czasami
cos ma byc na 100 cykli :), robi sie, modyfikuje zmienia kombinuje, a po
skonczeniu wychodzi ze jednak i tak wyszlo na 200 cykli :P.

---==<<Sc0rpi0>>==---