lunedì 11 giugno 2018

Floating point numbers in C64

Continua l'esplorazione di tutto quanto mi sono perso su C64

Quando ho iniziato a studiare l'assembler avevo Olivetti M24 su processore 8086 e sul C64 usavo solo BASIC (al limite integrato con Simon Basic per qualche routine grafica). L'assembler 8086 non e' banalissimo ma la cosa che piu' mi infastidiva era quella di non poter gestire in modo nativo la matematica dei numeri float ....a meno che di non possedere un costosissimo coprocessore matematico (che e' stato integrato solo dalla serie x486)

Con sorpresa ho scoperto che usare i numeri a virgola mobile e fare conti su C64 e' incredibilmente banale anche in assembler.

Per prima cosa i numeri float hanno una struttura di 5 byte e sono nel formato esponenziale base 2 ...quindi qualcosa del tipo x.xxx 2^y

byte 1 : esponente : il valore dell'esponente e' nel formato exp-129 (quindi 2^0 avra' il valore 81, valore piu' bassi indicano esponenti negativi)

byte 2-5 : mantissa

Convertire da formato float decimale a float C64

per fare poca fatica conviene usare direttamente la conversione di C64

Per esempio si digiti da BASIC
NEW
X = 3.14159

IND = PEEK(71)+256*PEEK(72)

PRINT PEEK(IND)
PRINT PEEK(IND+2)
PRINT PEEK(IND+3)
PRINT PEEK(IND+4)
PRINT PEEK(IND+5)

quale e' il significato. Diciamo al BASIC di salvare il valore di float di pi greco in una variabile. Nelle locazioni a pagina zero 71 e 72 c'e' il puntato alla zona di memoria dove viene inserito effettivanemente il numero (gia' decodificato in formato C64). Nel mio caso l'indirizzo e' $805 (esadecimale) se sei aggiungono altre variabile si vedra' che il puntatore si aggiorna

La risposta al comando e'
$82 $49 $0F $CF $82

ovvero la traduzione in formato float C64
Per fare la riprova si puo' usare questo programma assembler in ACME
in pratica si popola la'accumulatore di float FAC con il valore di pi greco, si chiama il kernal per la conversione del valore in una stringa ($BDDD) e poi si mostra la stringa a schermo
-----------------------------------------------
Float

!source "./std.a"
!to "float.prg", cbm
!sl "float.map"
!cpu 6510

*=$1000

LDFAC = $bba2


!macro LoadFAC .addr {
lda #<.addr
ldy #>.addr
jsr LDFAC
}

+LoadFAC pi

MOVFA ; sposta FAC in ARG


;JSR $E264 ;COSENO NON UTILIZZATA

JSR $BDDD ;Convert FAC to zero-terminated string representation at memory addresses $0100-$010A.

 LDY   #0

loop:   LDA   $100,Y
        BEQ   done
        JSR   $FFD2        
        INY
        BNE   loop          
done:

RTS

pi !by $82, $49, $0F, $CF, $82
-----------------------------------------------

Per la riconversione l'algoritmo (preso da qui) e' il seguente

  • Exponent: exp-128
  • Mantissa: (m4 >= 128 ? -1 : +1) * ((m4 | 0x80) >> 8 + m3 >> 16 + m2 >> 24 + m1 >> 32) 

La cosa interessante e' che da Assembler si possono chiamare anche le funzioni trigonometriche, le stesse condivise con BASIC, ma che risultano decisamente piu' veloci (vedi la chiama alla subroutine in $E264 corrispondente al coseno)

c'e' da osservare che la precisione dell'arrotondamento delle routine non e' ottimale (vedi questo link)