Chtel jsem ukazat co se vygeneruje kdyz se zapisne na vstup pro M4 Forth 'HEX UDOT', ale kdyz se ten trik ma odhalit priste tak nebudu predbihat.
2x volani new_line je spravne. Protoze ROM rutiny jsou kompikovane, a pro pozici na radku si ROM uklada hodnotu, ktera znaci kolik mista jeste ma pred koncem. Pry tak snaz pozna zda musi zalomit slovo. Pokud se zapise posledni znak, tak kurzor automaticky nepreskoci na dalsi radek, ale zustane na puvodnim s tim ze je tam nula. Myslim, ze ten duvod je, aby se neaktivoval skroll kdyz jen chceme zapsat cely radek (dostaneme se na konec), ale AZ POTE, co chceme ulozit dalsi znak.
Misto 2x new_line slo udelat i kurzor_vpravo (0x0D) a new_line (0x0D).
Kurzor vpravo a vlevo neni zabugovany v originalni romce tak funguje. Na rozdil od nahoru a dolu myslim. Kde se omylem ukoncuje rutina pres RET, ale pritom je to tokenizovany, takze se ma volat rutina pro konec. Nevim to jiste.
Jen jsem to nedavno resil, kdyz jsem psal ted fonty 5x8 pro ten M4 Forth, s tim ze jsem si musel presmerovat volani rst 0x10, coz je bolest, protoze to musite psat jako stream, takze vam chodi znak za znakem a vy musite menit stav a reagovat na vstup v zavislosti jaky je nastaveny stav. Zadny:
IF znak = INK THEN nacti si dalsi znak a zmeni ink.
Musite delat neco jako
IF stav = none AND znak = INK THEN zmen stav na read_ink
ELSE IF stav = read_ink THEN zmen ink a vynuluj stav.
BCD instrukcie sa na pocitanie meny v terajsej dobe uz moc nepouzivaju, long mode x86-64 ich uz nepodporuje.
Pouziva sa 64 bit integer s umelo vytvorenou desatinnou ciarkou, rozsah -92 233 720 368 547 758.08 az +92 233 720 368 547 758.07, co pre vecsinu aplikacii postacuje.
13. 4. 2023, 04:36 editováno autorem komentáře
Postgresový Numeric funguje na podobném principu jako BCD https://github.com/postgres/postgres/blob/master/src/backend/utils/adt/numeric.c
Pro mne, když jsem to viděl poprvé, hodně zajímavé bylo použití číslic 0..9999 místo obvyklých 0..9.
Nooo, 0..9 je obvykle pri pocitani na papieri. Tych 0..9999 umoznuje pouzit 16bit aritmetiku. IMHO, to preco je to vlastne ulozene ako pole 0..9999 hodnot je dane hlavne tym ako funguje prevod numeric na text, ak by to bolo ulozene ako jeden dlhy integer, tak by sa musela vzdy konvertovat cela frakna cast a stripnut na pocet desatinnych miest uvedeny vo formate...
V podstate je ale postgres mozne skopilovat tak aby pre ulozenie numeric pouzival zaklad 10 miesto 10000, riadky 75 a 95 linkovaneho zdrojaku, akurat je treba dat pozor na export povodnych dat, kde bude treba pouzit inserty miesto obvykleho copy.
13. 4. 2023, 14:45 editováno autorem komentáře
Postgres by default používá textový protokol (čísla se přenášejí jako text) pro vstup (INSERT, COPY FROM) i pro výstup (SELECT, COPY TO). Komentář ohledně změny NBASE zmiňuje problémy s binárním protokolem. Ten se zase může použít jak pro vstup, tak pro výstup. Ohledně vstupu může být binární protokol použitý i v parametrizovaném INSERTu (při volání API se nastavuje flag, jestli se parametry přenášejí binárně nebo nebo ne). Příkaz COPY FROM BINARY očekává binární protokol.
Opět, co může být trochu překvapující nebo matoucí, binární protokol nemusí nutně používat nativní formát. Je to navržené tak, aby byl binární protokol platformě nezávislý (např. interně používá pg jinou reprezentaci int64 na 32 bitech a na 64bitech).
S tím jsem ve svých začátcích s pg dost zápasil - a nechápal jsem, proč se víc nepoužívají nativní Cčkové reprezentace datových typů.
Jinak omlouvám se za offtopic :-)
Hodně záleží na datech, které máte. Pokud máte jenom čísla, a k tomu kratší stringy (to, co se označuje jako socio ekonomická data), tak tam zrychlení by příliš velké být nemělo. U dlouhých řetězců, dlouhých bytea nebo geodat si to zrychlení dovedu představit. Například u bytea vám binární protokol ušetří kódování, dekódování do hexakódu (CPU, a výsledný objem dat je poloviční).
Jinak IMHO každý RTC vrací čas v BCD.
Byly okolo toho nějaké problémy, když si to neuvědomili tvůrci SW pro registrační pokladny a od 1.1.2010 (asi chápete, proč toto datum) začaly pokladny generovat špatné záznamy s datem. Což nejde spravit a je to hodně velkej průser.
Otázka: kdo uhodne, jaké datum se vypisovalo namísto 1. 1. 2010?
v podstate spravne, ale tam byl jeste dalsi problem (za ktery by se hodilo nekoho vyhodit) - ten RTC vracel rok jako dve cifry, takze 09. No a programator si k tomu bez premysleni pridal "200" na zacatek. Takze najednou zacly pokladny psat 20016 :)
Popravde ani nevim jak se to vyresilo, ale predpokladam, ze na nekoho zaklekl financak...
Pobavil mě text nad obrázkem 11, který mluví o dvou kategoriích, načež vyjmenovává tři. A hned první výsledek v červeném rámečku na obrázku 11 tak trochu spadá do druhé i třetí kategorie.
0xA0 je sice nevalidní BCD, ale s přimhouřením oka se dá číst jako 10 0, tedy 100. Součet s jedničkou je tedy 101 a výsledek správně ukazuje 01 - ke správnému výsledku stačí v případě nastaveného C přidat zleva jedničku.
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.
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ů).
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.