Diky za clanek.
Ten tisk hex hodnoty mam uplne stejne. Jen se lisi instrukce pro 4 bitovy posun. Tam jsou mozne 3 varianty (rra,rrca,rlca).
U toho ZX cisla s plovouci carkou bych jen vyslovne upozornil ze ta mantisa je od 0.5 do skoro 1. A ne od 1 do skoro 2 jako v IEEE 754. Takze i ten Bias se pak chape jinak.
Ve chvili co zacnete delat deleni, vas zacne zajimat jak je ten exponent reseny a proc nekdy x --> 1/x --> 1/x --> != x. Pokud budete delat vlastni format tak se tomu budete snazit vyhnout.
PS: Registru IX bych se vyhybal co to jde... .)
Skoda ze neimplementovali misto IX to same pro SP (i pres tu pomalost a velikost by to bylo uzitecne). Jak pro funkce psane v C tak i pro Forth, kde to ale tolik neboli.
https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/83-print-fp-numbers.asm
ma 113 bajtu a kdyz se to napise na HL a udela par uprav tak to ma 73 bajtu (a slo by to stahnout jeste minimalne o bajt).
ENTRY_POINT equ $8000
ROM_CLS equ $0DAF
PRINT_FP equ $2DE3
org ENTRY_POINT
start:
call ROM_CLS ; smazání obrazovky a otevření kanálu číslo 2 (screen)
ld HL, fp0 ; adresa s pěticí bajtů FP hodnoty
ld B, 7
loop:
call print_fp_number ; tisk FP hodnoty na obrazovku s odřádkováním
djnz loop
ret
; mantisa+128 exponent <0.5..1)
fp0: db %00000000, %00000000, %00000000, %00000000, %00000000 ; 0
fp1: db %10000000, %00000000, %00000000, %00000000, %00000000 ; 0.5
fp2: db %10000000, %10000000, %00000000, %00000000, %00000000 ;-0.5
fp3: db %10000000, %01000000, %00000000, %00000000, %00000000 ; 0.75
fp4: db %10000001, %01000000, %00000000, %00000000, %00000000 ; 1.5
fp5: db %10000001, %01000000, %00000000, %00000001, %00000000 ; 1.5000001
fp6: db %10000001, %00111111, %11111111, %11111111, %00000000 ; 1.4999999
print_fp_number:
; HL pointer to FP
; Nici: AF,DE,DE',BC'
; Meni: HL +=5
push BC
ld DE,($5C65) ; Fetch the address of the first location above the present stack (STKEND).
ld BC, 5
ldir
LD ($5C65),DE
push HL
; zachova jen IX, IY, AF' a HL'
call PRINT_FP ; vytisknout FP hodnotu uloženou na vrcholu zásobníku
pop HL
pop BC
new_line:
ld A, 0x0d ; kód znaku pro odřádkování
rst 0x10 ; zavolání rutiny v ROM
ret ; návrat ze subrutiny
end ENTRY_POINT
Díky za skvělý článek. Jako myšlenkový experiment mě napadlo, co kdybych chtěl tisknout šestnáctková čísla s malými místo velkými písmeny.
Došel jsem k závěru, že asi nejlépe přidat na konec or $20 - číslicím to neublíží protože 3 or 2 je stále 3, ale u písmen to přičte 0x20, což přepne na rejstřík malých písmen.
"40 bitů bylo pravděpodobně zvoleno z toho důvodu, že všech pět univerzálních pracovních registrů má dohromady přesně tuto šířku"
- Tohle je podle mne hodně vedle. Řekl bych že hlavním důvodem byl fakt, že Microsoftí Basic v té době používal 40-bitový formát a ten byl považován za de facto standard. Původní FP formát pro Altair byl 32 bitový, nicméně 24 bitů mantisy stačí na zhruba 6 decimálních čísel a to se rychle ukázalo jako nedostačující. Microsoft šel cestou nejmenšího odporu a rozšířil mantisu na 32 bitů (zhruba 9 decimálních čísel).
Při implementaci FP vás netrápí ani tak celková velikost čísla, ale velikost mantisy, protože právě tu je třeba normalizovat, denormalizovat, násobit a dělit.
Krásná ukázka silné stránky Z80 je právě násobení dvou 32 bitových mantis v ROM ZX Spectra
MLT_LOOP:
jr nc, NO_ADD
add hl, de
exx
add hl, de
exx
NO_ADD:
exx
rr h
rr l
exx
rr h
rr l
STRT_MLT:
exx
rr b
rr c
exx
rr c
rra
djnz MLT_LOOP
Je to klasický shift-add algoritmus, nicméně s pár chytrýmy detaily:
a) shift doprava umožňuje příčíst bity, které přetekly z dolních 32 bitů výsledku do horních 32 bitů, aniž by se počítalo se 64 bitovými čísly
b) využití stínových registrů, celkem rutina pracuje se třemi 32bitovými hodnotami, H'L'HL je výsledkem, B'C'CA je násobitel, D'E'DE je násobenec a jedním osmibitovým počítadlem.
c) hezká mikrooptimalizace v případě prohození B a A registru, kdy je A rychlejší při bitové rotaci a naopak B je díky instrukci vhodné jako počítadlo.