Vlákno názorů k článku Aritmetické operace s hodnotami uloženými binárně i ve formátu BCD od _dw - Upravil jsem jeste https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/77-add-hex-numbers.asm bez toho, ze bych...

  • Článek je starý, nové názory již nelze přidávat.
  • 13. 4. 2023 17:38

    _dw

    Upravil jsem jeste https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/77-add-hex-numbers.asm bez toho, ze bych pouzil trik s DAA.

    Zvlastni, ze se i pres to, ze vypisujes list toho vysledku, se nikdo nepozastavuje nad tim, ze pro druhy CALL pouzivas JP kvuli optimalizaci, ale pritom delas "nulovy" skok.

    8028:C32B80     JP 802B
    802B:           label print_hex_digit

    Protoze mas spravne umistenou rutinu print_hex_number nad print_hex_digit. Takze ten kod automaticky spravne "propadne" do print_hex_digit. Neni potreba vubec nic delat. Spravne umisteni rutin za sebou je v asembleru dulezite. Nekdy se dokonce o to misto nad pouzivanou rutinou poperou mezi sebou vic rutin a musis si zvolit, ktera se pouzije casteji.

    Dalsi vec je pouziti call na rutiny new_line a space. Obe rutiny zabiraji bez RET 3 bajty (nevim jestli tohle ale normalni clovek hned vidi) a ten CALL samotny je na 3 bajty taky. Takze je to zbytecnych 17 taktu na CALL + 10 taktu na RET a kdyz se to "inkludne" tak ti odpadne i jeden bajt za ten RET.

    Dalsi vec je trojnasobne pouziti B = 11. To uz se ti vyplati pouzit BC = 11*257 a k tomu 2x pouzit B = C.

    Posledni vec je to neohrabene testovani zda mohu tisknout mezeru za cislo.

    print_numbers:
    next_item:
            ld   A, (HL)           ; načíst hodnotu z pole
            inc  HL                ; přechod na další prvek pole
            call print_hex_number  ; vytisknout hexa hodnotu
            ld   A, B
            cp   1                 ; za poslední hodnotou už nechceme tisknout mezeru
            jr   Z, skip           ; přeskočení tisku mezery u poslední hodnoty
            call space
    skip:
            djnz next_item         ; zpracování dalšího prvku pole
            ret

    To jde napsat mnohem lepe pouhym preskupenim kodu. Tisk mezery das pred fci a vstupni bod bude az za tiskem mezery. Ale smycka bude skakat/se vracet na tisk mezery. Tim zarucis, ze mezera se netiskne jako prvni znak, ani jako posledni.

    ENTRY_POINT   equ $8000
    ROM_CLS       equ $0DAF
    OUT_NUM_1     equ $1A1B
    
    
            org ENTRY_POINT
    
    start:
            call ROM_CLS           ; smazání obrazovky a otevření kanálu číslo 2 (screen)
    
            ld   HL, array1        ; vytištění 11 hodnot z pole array1 v hexadecimálním formátu
            ld   BC, 0x0B0B        ; B = 11, C = 11
            call print_numbers
    
            ld   HL, array2        ; vytištění 11 hodnot z pole array2 v hexadecimálním formátu
            ld   B, C
            call print_numbers
    
                                   ; odřádkování
            ld   A, 0x0d           ; kód znaku pro odřádkování
            rst  0x10              ; zavolání rutiny v ROM
            ld   A, 0x0d           ; kód znaku pro odřádkování
            rst  0x10              ; zavolání rutiny v ROM
    
            ld   DE, array1        ; adresa pole s první řadou sčítanců
            ld   HL, array2        ; adresa pole se druhou řadou sčítanců
            ld   B, C
    next_add:
            ld   A, (DE)           ; načíst první sčítanec z pole
            add  A, (HL)           ; načíst druhý sčítanec z pole
            inc  DE                ; posun ukazatele na další sčítanec
            inc  HL                ; dtto
            call print_hex_number  ; vytisknout hexa hodnotu výsledku
            ld   A, 32             ; kód mezery
            rst  0x10              ; zavolání rutiny v ROM
            djnz next_add          ; kontrola počtu zobrazených výsledků
            ret
    
    ; podprogram pro tisk sekvence numerických hodnot v hexadecimálním formátu
    ; vstupy:
    ;         HL - ukazatel na pole s hodnotami
    ;         B - počet hodnot (délka pole)
    next_item:
            ld   A, 32             ; kód mezery
            rst  0x10              ; zavolání rutiny v ROM
    print_numbers:                 ; entry point
            ld   A, (HL)           ; načíst hodnotu z pole
            inc  HL                ; přechod na další prvek pole
            call print_hex_number  ; vytisknout hexa hodnotu
            djnz next_item         ; zpracování dalšího prvku pole
            ret
    
    array1:
            db 0x00, 0x01, 0x05, 0x09, 0x09, 0x09, 0x10, 0x10, 0xa0, 0xfe, 0xff
    
    array2:
            db 0x01, 0x02, 0x05, 0x01, 0x06, 0x07, 0x09, 0x10, 0x01, 0x01, 0x01
    
    
    print_hex_number:
            push AF             ; uschovat A pro pozdější využití
            rrca                ; rotace o čtyři bity doprava
            rrca
            rrca
            rrca
            and  $0f            ; zamaskovat horní čtyři bity
            call print_hex_digit; vytisknout hexa číslici
    
            pop  AF             ; obnovit A
            and  $0f            ; zamaskovat horní čtyři bity
    ;       jp print_hex_digit  ; vytisknout hexa číslici a návrat z podprogramu
    ;       fall to print_hex_digit
    print_hex_digit:
            cp   0x0a           ; test, jestli je číslice menší než 10
            jr c, print_0_to_9  ; ok, hodnota je menší než 10, budeme tedy tisknout desítkovou číslici
            add  A, 65-10-48    ; ASCII kód znaku 'A', ovšem začínáme od desítky, ne od nuly (+ update pro další ADD)
    print_0_to_9:
            add  A, 48          ; ASCII kód znaku '0'
            rst  0x10           ; zavolání rutiny v ROM pro tisk jednoho znaku
            ret                 ; návrat ze subrutiny
    
    
    end ENTRY_POINT

    Ze 118 bajtu se dostanes na 101 bajtu.

  • 14. 4. 2023 10:56

    Pavel Tišnovský
    Zlatý podporovatel

    no vidíš, toho jsem si nevšiml. Zase ale pokud by někdo neznalý toho triku udělal refactoring a strčil si subrutiny někam do dalšího souboru, už by to takto nešlo (ale tím v žádném případě, neříkám, že nemáš pravdu - máš!)

    OT: se tak dívám, že příště budu psát o Tvé doméně, pokud se tedy dívám na Tvoje repo s FP subrutinami (a není to shoda nicků).

  • 15. 4. 2023 16:50

    _dw

    Jestli tim trikem myslis nepouzivat skok a umistit rutina za sebe tak jsem to zkousel jeste krome komentare ze ty rutiny jsou "slepene" :

    ; fall to print_hex_digit

    osetrit pres nejaky

    if ($ != print_hex_digit)
    .error Blabla
    endif

    ale to pasmo nezvladne protoze print_hex_digit pry jeste nezna i kdyz lezi pod tim a nekdy se sam prepne do vic pruchodu.

    Tak jsem se to snazil obejit pres

    omg equ print_hex_digit
    if ($ != omg)
    .error Blabla
    endif

    ale to selze stejne uz u omg. Mozna to jde jinym trikem, ale nevim jak.

    PS: Tu bfloat, danagy a binary16 floating-point knihovnu jsem psal. Na FB existovala (mozna jeste existuje) skupina lidi z celeho sveta stale programujici pro ZX. A krome ruznych soutezi tam ukazoval Daniel Nagy i Ray tracing pro ZX. Chtel jsem jen vedet zda to nebude lepsi kdyz pouziji jiny format, ale stale 16 bitovy. On zvolil 8 bitu mantisu ja testoval jak 9 bitu tak 7 bitu. Drzel jsem se jeho navrhu "api" a drzel vstupy a vystupy ce stejnych registrech. Delal jsem to zamerene na rychlost, ale snazil se zaroven, aby pokud to jde po operaci byly vzdy vsechny bity mantisy i ten nejnizsi spravne zaokrouhlene. Co on nemel, takze jsem nakonec prepsal i jeho kod. Pak jsem to zpomalil uz jen tim, ze jsem vracel pri preteceni nebo podteceni priznaky.

    Psal jsem to cely rok, nez se mi podarilo konecne vygenerovat ten obrazek. Byla tam spousta triku jak to delat pres male tabulky. Zjistil jsem ze napriklad nasobeni je snazsi nez scitani a odcitaniu FP. Deleni je problem, ale jde to pres 1/mantisa tabulku s tim, ze nekdy nejnizsi bit neni dobre. LN je silenost. Delat to nejakym vzorcem pomoci uz vytvorenych operaci jen ztracis bity a bude to pomale. Takze pouzit tabulku s mezivysledky a nejaka interpolace? Je tam i zavislost na exponentu. U nekterych vstupnich hodnot bylo nakonec vic bitu spatne na vystupu.

    Z toho githubu se da spoustu veci odvodit, ale neni tam vsechno. Treba nektere tabulky jsem vytvarel pomoci spatlaneho programu v C co me to pocital iteracne (nahodne jsem drobne menil hodnoty tabulky a opravoval zbytek pro nejmensi chyby, mel vic variant a pak nejlepsi ponechal a opakoval) a snazil se mit pro vsechny vstupy minimalni chybu nebo zadnou na vystupu. Aby probehlo vsude spravne zaokrouhleni. Zkousel jsem ruzne verze tabulek. Pocital to kolikrat hodiny co byl v praci na modernim CPU. Proste neco co by pred 40 lety neslo udelat.

    Ale uz jsou to 3 roky, moc si toho nepamatuji. Jen ze danagyho format to vyhral. binary16 melo vubec problem to vykreslit, tam uz temer nevyslo pro rozsah exponentu. A byl pomaly a i kdyz nejpresnejsi rozbrazeni stale mel chyby oproti bezchybne basic verzi. Bfloat nepresny a stejne rychly jak Danagy. Ale mensi tabulky, kdyz je mensi mantisa.

    1 BORDER 0: PAPER 0: INK 9: BRIGHT 1: CLS: POKE 23672,0: POKE 23673,0: POKE 23674,0
    10 LET spheres=2: IF spheres THEN DIM c(spheres,3): DIM r(spheres): DIM q(spheres)
    20 FOR k=1 TO spheres: READ c(k,1),c(k,2),c(k,3),r: LET r(k)=r: LET q(k)=r*r: NEXT k
    30 DATA -0.3,-0.8,3,0.6
    40 DATA 0.9,-1.1,2,0.2
    50 FOR i=0 TO 175: FOR j=0 TO 255
    60 LET x=0.3: LET y=-0.5: LET z=0
    70 LET dx=j-128: LET dy=88-i: LET dz=300: LET dd=dx*dx+dy*dy+dz*dz
    80 GO SUB 100: NEXT j: NEXT i
    90 PAUSE 1: LET s=PEEK 23672+256*PEEK 23673+65536*PEEK 23674: LET s=s/50: LET m=INT (INT s/60): LET h=INT (m/60): PRINT "Time: ";h;"h ";m-60*h;"min ";INT((s-60*m)*100)/100;"s ";PAUSE 0:STOP
    100 LET n=-(y>=0 OR dy<=0): IF NOT n THEN LET s=-y/dy
    110 FOR k=1 TO spheres
    120 LET px=c(k,1)-x: LET py=c(k,2)-y: LET pz=c(k,3)-z
    130 LET pp=px*px+py*py+pz*pz
    140 LET sc=px*dx+py*dy+pz*dz
    150 IF sc<=0 THEN GO TO 200
    160 LET bb=sc*sc/dd
    170 LET aa=q(k)-pp+bb
    180 IF aa<=0 THEN GO TO 200
    190 LET sc=(SQR bb-SQR aa)/SQR dd: IF sc<s OR n<0 THEN LET n=k: LET s=sc
    200 NEXT k
    210 IF n<0 THEN RETURN
    220 LET dx=dx*s: LET dy=dy*s: LET dz=dz*s: LET dd=dd*s*s
    230 LET x=x+dx: LET y=y+dy: LET z=z+dz
    240 IF n=0 THEN GO TO 300
    250 LET nx=x-c(n,1): LET ny=y-c(n,2): LET nz=z-c(n,3)
    260 LET nn=nx*nx+ny*ny+nz*nz
    270 LET l=2*(dx*nx+dy*ny+dz*nz)/nn
    280 LET dx=dx-nx*l: LET dy=dy-ny*l: LET dz=dz-nz*l
    290 GO TO 100
    300 FOR k=1 TO spheres
    310 LET u=c(k,1)-x: LET v=c(k,3)-z: IF u*u+v*v<=q(k) THEN RETURN
    320 NEXT k
    330 IF (x-INT x>.5)<>(z-INT z>.5) THEN PLOT j,i
    340 RETURN

    Ta trva vic jak 8 hodin a 46 minut.