 
View Full Version : [NASM x86] Convertire uint64 in double
Ciao a tutti,
come potete vedere nella mia firma sto scrivendo un sistema operativo: Cosmos!
E' scritto in C# e per certi versi è la versione Open Source dei progetti di ricerca Singularity e Midori.
La differenza rispetto a un sistema operativo classico è che il runtime .NET è integrato nel Sistema Operativo stesso e tutte le istruzioni IL devono essere convertite in assembler prima ancora di creare il kernel.
E' un po di tempo che sto impazzendo con l'instuzione conv.r.un che prende in input un uint32 o un uint64 e lo converte in double. Purtroppo non esiste né per la FPU x87 né SSE un'istruzione diretta per effettuare questa conversione quindi fa fatta "a mano".
Questo è quello che ho cercato di fare io:
  # in ESP c'è un ulong, prendiamo la parte alta e la mettiamo in EAX 
  mov EAX, [ESP + 32]
  # Convertiamo l'ulong in double e salviamo in ST0
  fild  qword ptr [ESP]
  # Shift a destra di EAX fino al byte 31
  shr EAX, 31
  # Confrontiamo EAX con 0
  cmp EAX, 0
  # Se è 0 abbiamo finito saltiamo in LabelSign_Bit_Unset
  jpe LabelSign_Bit_Unset
  # Se è 1 dobbiamo togliere il segno dal double
  fadd  dword ptr __ulong2double_const2
  # salviamo il risultato in ESP
  LabelSign_Bit_Unset:
  fstp ESP
L'idea di questo codice è convertiamo l'ulong con fild come se fosse un normale long e poi se c'era il segno (un 1 nel 31° bit) viene 'fixato' con fadd con un'opportuna costante... __ulong2double_const2.
Il problema è proprio che non trovo qual è questa constante per ora ho provato con 0x0000000000405BC0 (c'è una matematica complicatissima dietro, ma ve la evito :D), ma non va una fava :Prrr: 
Idee? Soluzioni? Insulti?
||ElChE||88
25-06-2016, 19:59
Non conosco NASM ma in MASM dovrebbe essere qualcosa del genere:
.386
.model flat
.data
uint64add dd 5F800000h
.code
mov eax, dword ptr [esp+4]
fild qword ptr [esp]
test eax, eax
jns short isuint32
fadd ds:uint64add
isuint32:
fstp qword ptr [esp]
end
cdimauro
25-06-2016, 20:58
E' un po di tempo che sto impazzendo con l'instuzione conv.r.un che prende in input un uint32 o un uint64 e lo converte in double. Purtroppo non esiste né per la FPU x87 né SSE un'istruzione diretta per effettuare questa conversione quindi fa fatta "a mano".
Idee? Soluzioni? Insulti?
Per x87 t'hanno già risposto, mentre per SSE dovresti aprire il manuale dell'architettura x86 :D e cercare fra le istruzioni di conversione, perché ne troverai due che sono utili per il tuo scopo:
CVTSI2SD xmm, r/m32   ; Da sint32 a double
CVTSI2SD xmm, r/m64   ; Da sint64 a double
Ovviamente sono con segno, per cui devi utilizzare degli accorgimenti visto che hai uint32 e uint64:
- se l'MSB del valore intero è zero, puoi convertirlo così com'è;
- altrimenti rimuovi l'MSB, lo converti in double, e poi gli aggiungi 2^31 o 2^63 (con opportune costanti double).
||ElChE||88
26-06-2016, 06:05
.386
.mmx
.xmm
.model flat
.data
align 16
xmm_sub dd 0, 43300000h, 0, 45300000h
align 16
xmm_interleave dd 43300000h, 45300000h, 0, 0
.code
movq xmm1, qword ptr [esp]
punpckldq xmm1, xmmword ptr [ds:xmm_interleave]
subpd xmm1, xmmword ptr [ds:xmm_sub]
pshufd xmm0, xmm1, 00011110b
addpd xmm0, xmm1
movq qword ptr [esp], xmm0
end
Non conosco NASM ma in MASM dovrebbe essere qualcosa del genere:
.386
.model flat
.data
uint64add dd 5F800000h
.code
mov eax, dword ptr [esp+4]
fild qword ptr [esp]
test eax, eax
jns short isuint32
fadd ds:uint64add
isuint32:
fstp qword ptr [esp]
end
Questa è la tua versione (credo) tradotta in NASM / x86:
       mov dword EAX, [ESP + 4]
       fild qword [ESP]
       test dword EAX, EAX
       JNS near LabelSign_Bit_Unset
       fadd qword [0x5F800000]
       LabelSign_Bit_Unset:
       fstp dword [ESP]
purtroppo non funziona! Il valore ottenuto non ha proprio senso se debbo credere a Cosmos 0x0000FFFF?
@cdimauro
Attualmente il compilatore di Cosmos ha come target x86 a 32 Bit anch'io speravo di poter usare SSE per tutto, ma pare che quando si opera sui long non sia possibile, l'istruzione CVTSI2SD opera sempre su sint32, infatti quella che dovrei usare è CTVSQ2SD, ma esiste solo su IA64!
O almeno così questi signori mi hanno convinto:
http://forum.osdev.org/viewtopic.php?f=1&t=30324
La seconda versione che usa punpckldq mi spaventa un po' avevo visto una versione simile usata da Clang, ma sinceramente non so proprio come tradurla in NASM.
L'idea di usare NASM che mi pare non usi nessuno per creare l'assembler mi sembra una scelta proprio sbagliata! Lavoriamo su Windows con Visual Studio (quindi no per ora di creare Cosmos su Linux non ce ne frega nulla) perché non usare l'assembler della Microsoft MASM che sembra molto più avanzato? E soprattutto il cui assembler è molto più conosciuto? E che posso chiedere a Visual Studio di mostrarmi usando i breakpoint?
Comunque stiamo provando a scrivere il nostro High Level Assembler che sarà integrato con C# e che si chiama X# (X sta per X86, in futuro esisterà anche A# per ARM!), infatti io non scrivo NASM direttamente, ma questo:
      // Save the high part of the ulong in EAX (we cannot move all of ESP as it has 64 bit size)
      XS.Set(EAX, ESP, sourceIsIndirect: true, sourceDisplacement: 4);
      XS.FPU.IntLoad(ESP, isIndirect: true, size: RegisterSize.Long64);
      // Get its highest bit to check if the value was signed or unsigned
      //XS.ShiftRight(EAX, 31);
      //XS.Compare(EAX, 0x00);
      //XS.Jump(ConditionalTestEnum.Equal, LabelSign_Bit_Unset);
      XS.Test(EAX, EAX);
      XS.Jump(ConditionalTestEnum.NotSign, LabelSign_Bit_Unset);
      //XS.LiteralCode(@"fadd qword [__ulong2double_const3]");
      XS.LiteralCode(@"fadd qword [0x5F800000]");
      XS.Label(LabelSign_Bit_Unset);
      XS.FPU.FloatStoreAndPop(ESP, isIndirect: true, size:RegisterSize.Long64);
che viene poi trancompilato in NASM e "ibridato" con il kernel!
Ma il vero X# è ancora meglio:
https://en.wikipedia.org/wiki/X_Sharp
ma dobbiamo studiare come embeddarlo dentro a C#...
||ElChE||88
26-06-2016, 15:45
purtroppo non funziona! Il valore ottenuto non ha proprio senso se debbo credere a Cosmos 0x0000FFFF?
Strano, a me funziona. Nella tua versione NASM il fstp è sbagliato (dword invece di qword), però ho visto che nella versione X# è segnato giustamente come qword.
@cdimauro
Attualmente il compilatore di Cosmos ha come target x86 a 32 Bit anch'io speravo di poter usare SSE per tutto, ma pare che quando si opera sui long non sia possibile, l'istruzione CVTSI2SD opera sempre su sint32, infatti quella che dovrei usare è CTVSQ2SD, ma esiste solo su IA64!
L'alternativa è usarne 2:
.386
.mmx
.xmm
.model flat
.data
dq_add dq 0
dq_mul dq 41F0000000000000h
.code
mov eax, dword ptr [esp]
cvtsi2sd xmm0, eax
shr eax, 1Fh
addsd xmm0, qword ptr [ds:dq_add+eax*8]
mov eax, dword ptr [esp+4]
test eax, eax
jz short store
cvtsi2sd xmm1, eax
shr eax, 1Fh
addsd xmm1, qword ptr [ds:dq_add+eax*8]
mulsd xmm1, qword ptr [ds:dq_mul]
addsd xmm0, xmm1
store:
movq qword ptr [esp], xmm0
end
Strano, a me funziona. Nella tua versione NASM il fstp è sbagliato (dword invece di qword), però ho visto che nella versione X# è segnato giustamente come qword.
Grazie! Sì c'era un errore in X#... il backend ignorava il parametro passato e credo dword sia messo di default, questo avrebbe dovuto risolvere tutto ed infatti ero tutto emozionato (erto ho pensato prendevo solo metà del double...
) ed invece no :cry:
Ho pensato che fosse il little endian a fregarmi e così ho invertito la tua constante che è diventata 0x00000805F, beh il risultato ottenuto è F0BF, ma dovrebbe essere F043.
Il valore da me usato è uno che sicuramente dovrebbe risultare segnato: -1 che visto come ulong è un numeraccio: 18446744073709551615 (in hex è ovviamente 8 volte F) eppure sembra che EAX sia non segnato visto che la fadd non ha effetto! Non capisco...
L'alternativa è usarne 2:
.386
.mmx
.xmm
.model flat
.data
dq_add dq 0
dq_mul dq 41F0000000000000h
.code
mov eax, dword ptr [esp]
cvtsi2sd xmm0, eax
shr eax, 1Fh
addsd xmm0, qword ptr [ds:dq_add+eax*8]
mov eax, dword ptr [esp+4]
test eax, eax
jz short store
cvtsi2sd xmm1, eax
shr eax, 1Fh
addsd xmm1, qword ptr [ds:dq_add+eax*8]
mulsd xmm1, qword ptr [ds:dq_mul]
addsd xmm0, xmm1
store:
movq qword ptr [esp], xmm0
end
A questo punto però la domanda sorge spontanea è davvero più veloce che usare la FPU x87? Sono molte più istruzioni, ci sono anche delle moltiplicazioni, c'è comunque un branch!
La vera soluzione sarebbe usare AVX, ma già abbiamo tagliato via tutto quello che c'è prima del PIII usando SSE2 per avere le istruzioni di conversione
int <--> double così funzioneremmo solo su PC dal 2007 in poi :cry:
||ElChE||88
26-06-2016, 18:50
Esattamente che valori hai in input e output?
In input ho -1 che però espresso in ulong è 18446744073709551615 il risultato atteso è un double il cui valore in hex è F043 (ovviamente stampato come double è 18446744073709551615.0) invece ottengo F0BF (-1.0 come double). Ho pensato di usare -1 come "caso più semplice" ho un bit pattern con tutti 1 e invece il test dice che ho uno 0 in MSB... non ha senso!
Se potessi provare anche tu mi fai un piacere...
Sono ormai quasi convinto che è un bug o di NASM (che genera x86 errato) o di Bochs che non setta correttamente EAX!
cdimauro
26-06-2016, 22:00
@cdimauro
Attualmente il compilatore di Cosmos ha come target x86 a 32 Bit anch'io speravo di poter usare SSE per tutto, ma pare che quando si opera sui long non sia possibile, l'istruzione CVTSI2SD opera sempre su sint32, infatti quella che dovrei usare è CTVSQ2SD, ma esiste solo su IA64!
A questo punto però la domanda sorge spontanea è davvero più veloce che usare la FPU x87? Sono molte più istruzioni, ci sono anche delle moltiplicazioni, c'è comunque un branch!
La vera soluzione sarebbe usare AVX, ma già abbiamo tagliato via tutto quello che c'è prima del PIII usando SSE2 per avere le istruzioni di conversione
int <--> double così funzioneremmo solo su PC dal 2007 in poi :cry:
Perdonami per la critica, ma onestamente ritengo sia del tutto insensato avere come target x86 a 32-bit, quando è da 14 anni ormai che circolano macchine a 64 bit, e quelle strettamente a 32 bit ormai non so nemmeno se siano commercializzate.
Se volete sperimentare coi soli puntatori a 32-bit, allora utilizzate l'ABI x32 (https://en.wikipedia.org/wiki/X32_ABI), che gira sempre in modalità x64 e, quindi, ne consente di sfruttare tutti i benefici, ma mantiene soltanto i puntatori a 32 bit, per alleggerire la pressione su data cache et similia.
Comunque stiamo provando a scrivere il nostro High Level Assembler che sarà integrato con C# e che si chiama X# (X sta per X86, in futuro esisterà anche A# per ARM!), infatti io non scrivo NASM direttamente, ma questo:
      // Save the high part of the ulong in EAX (we cannot move all of ESP as it has 64 bit size)
      XS.Set(EAX, ESP, sourceIsIndirect: true, sourceDisplacement: 4);
      XS.FPU.IntLoad(ESP, isIndirect: true, size: RegisterSize.Long64);
      // Get its highest bit to check if the value was signed or unsigned
      //XS.ShiftRight(EAX, 31);
      //XS.Compare(EAX, 0x00);
      //XS.Jump(ConditionalTestEnum.Equal, LabelSign_Bit_Unset);
      XS.Test(EAX, EAX);
      XS.Jump(ConditionalTestEnum.NotSign, LabelSign_Bit_Unset);
      //XS.LiteralCode(@"fadd qword [__ulong2double_const3]");
      XS.LiteralCode(@"fadd qword [0x5F800000]");
      XS.Label(LabelSign_Bit_Unset);
      XS.FPU.FloatStoreAndPop(ESP, isIndirect: true, size:RegisterSize.Long64);
che viene poi trancompilato in NASM e "ibridato" con il kernel!
Ma il vero X# è ancora meglio:
https://en.wikipedia.org/wiki/X_Sharp
ma dobbiamo studiare come embeddarlo dentro a C#...
Un assembler di alto livello, insomma. Ne avevo realizzato uno con concetti simili una ventina d'anni fa, per 65C02 e successivamente per PIC Microchip. :D
Perdonami per la critica, ma onestamente ritengo sia del tutto insensato avere come target x86 a 32-bit, quando è da 14 anni ormai che circolano macchine a 64 bit, e quelle strettamente a 32 bit ormai non so nemmeno se siano commercializzate.
Anche Galileo e Edison sono a 64 Bit? Il target vero di Cosmos è il mercato embedded il concetto rispetto a Linux è inverso lì prendi un sistema pensato per fare da server e lo "violenti" togliendone il 90% fino ad ottenere un OS più o meno funzionante da mettere dentro un PC embedded (o uno smartphone che poi è lo stesso ormai!), Cosmos invece di suo è solo gestione memoria ed I/O e tu aggiungi il resto!
Se volete sperimentare coi soli puntatori a 32-bit, allora utilizzate l'ABI x32 (https://en.wikipedia.org/wiki/X32_ABI), che gira sempre in modalità x64 e, quindi, ne consente di sfruttare tutti i benefici, ma mantiene soltanto i puntatori a 32 bit, per alleggerire la pressione su data cache et similia.
Comunque credo usino IA32 perché forse è quello più semplice per iniziare c'è poi - ovviamente - l'intenzione di fare una versione per IA64... e beh in futuro anche per ARM! Come tutti i sistemi operativi che si rispettano sarà portabile,  ma questa volta le applicazioni una volta scritte gireranno su qualunque CPU.
Sì x32 è una strada che mi piacerebbe percorrere anche se non ho capito se in ambito .NET è la stessa cosa del flag /prefer32. Di certo "sprecare" memoria per i puntatori a 64 bit quando le applicazioni che hanno bisogno di più > 4 GB di memoria si contano sulle dita di una mano pare un po' un spreco. Sì un kernel a 64 (o 32x) che permette di far girare le applicazioni che lo richiedono a 32 Bit o 64 Bit sarebbe l'ideale!
Questo è quello che mi eccita di più di questo progetto non abbiamo 30 anni di retrocompatibilità tra le scatole e possiamo pensare a cose veramente nuove. Per esempio 32x che a me pare un'ottima idea su Linux non se lo è c*gato nessuno perché c'erano i 30 di x86 e visto che era incompatibile la gente si è chiesta qual è il senso?
Un assembler di alto livello, insomma. Ne avevo realizzato uno con concetti simili una ventina d'anni fa, per 65C02 e successivamente per PIC Microchip. :D
Fico! Come si chiamava?
cdimauro
26-06-2016, 23:49
Anche Galileo e Edison sono a 64 Bit? Il target vero di Cosmos è il mercato embedded il concetto rispetto a Linux è inverso lì prendi un sistema pensato per fare da server e lo "violenti" togliendone il 90% fino ad ottenere un OS più o meno funzionante da mettere dentro un PC embedded (o uno smartphone che poi è lo stesso ormai!), Cosmos invece di suo è solo gestione memoria ed I/O e tu aggiungi il resto!
OK, in questo caso cambia tutto... ma anche per te. :D
Galileo supporta soltanto l'ISA Pentium, e dunque hai soltanto l'FPU e nessuna unità SIMD (nemmeno MMX).
Con Edison ti va molto meglio, perché l'ISA Silvermont, e dunque fino alle SSE4.2. Ma niente AVX, purtroppo. Solo che non so se specificamente per Edison sia disponibile anche la modalità a 64 bit.
Comunque credo usino IA32 perché forse è quello più semplice per iniziare c'è poi - ovviamente - l'intenzione di fare una versione per IA64... e beh in futuro anche per ARM! Come tutti i sistemi operativi che si rispettano sarà portabile,  ma questa volta le applicazioni una volta scritte gireranno su qualunque CPU.
Chiaro. Quindi dovrà girare anche sul 6510 del Commodore64. :D
Sì x32 è una strada che mi piacerebbe percorrere anche se non ho capito se in ambito .NET è la stessa cosa del flag /prefer32.
Non credo che .NET la supporti. AFAIK è solo disponibile per Linux.
Ovviamente nessuno t'impedisce di realizzare un backend .NET per x32, magari partendo da quello x64.
Di certo "sprecare" memoria per i puntatori a 64 bit quando le applicazioni che hanno bisogno di più > 4 GB di memoria si contano sulle dita di una mano pare un po' un spreco. Sì un kernel a 64 (o 32x) che permette di far girare le applicazioni che lo richiedono a 32 Bit o 64 Bit sarebbe l'ideale!
Questo è quello che mi eccita di più di questo progetto non abbiamo 30 anni di retrocompatibilità tra le scatole e possiamo pensare a cose veramente nuove. Per esempio 32x che a me pare un'ottima idea su Linux non se lo è c*gato nessuno perché c'erano i 30 di x86 e visto che era incompatibile la gente si è chiesta qual è il senso?
Esattamente. Ma con un runtime .NET il discorso cambia completamente, e il supporto sarebbe trasparente.
Fico! Come si chiamava?
Galileo, ma non l'ho mai rilasciato in giro: l'ho creato per un paio di progetti a cui ho lavorato.
Ma sapete che 'sto Cosmos mi sta intrippando? Quasi quasi, appena mi libero un po' con la tesi (Aaaaaah, le catene di Markov... :fagiano: ) mi unisco a voi :D
Ieri sera abbiamo messo Bochs in modalità debug e siamo andati a vedere step per step cosa c'era nei registri vi faccio un breve riassunto:
 Nello stack c'era l'unlong FFF... diviso in 2 parti essendo - ovviamente - lo stack x86 composto da parti a 32 Bit
 Dopo la mov in EAX c'era la parte alta del nosto ulong (0xFFFF)
 Dentro il registo ST0 della CPU era inserito -1.0 ma in esadecimale non era il valore che mi aspettavo (0xBFFF invece di 0xF0BF) forse perchè la FPU era in extended precision? Gli strani floating point a 80 bit...
 Il jump era correttamente non eseguito perchè EAX aveva il segno (!)
 Ma la fadd non faceva assolutamente nulla e quindi dopo quell'istruzione in ST0 c'era ancora -1.0 / 0xBFFF!
 fstp convertiva correttamente 0xBFFF in 0xF0BF e lo copiava nello stack
Quindi questa è la domanda da un milione di Euro: come può un'addizione non avere effetto? L'unico caso in matematica è quando l'addendo vale 0, ma qui valeva 0x5F800000!
Forse è perché la constante è un double mentre in FP0 c'è un long double? Vede solo gli ultimi 5 zeri?
OK, in questo caso cambia tutto... ma anche per te. :D
Beh non è che ci lavoro solo io eh! Non sono nemmeno il capo del team io mi sono aggiunto solo di recente (volevo il cursore quadrato e blikante tipo C=64 e poi son finito a scrivere assembler in C# :p).
Galileo supporta soltanto l'ISA Pentium, e dunque hai soltanto l'FPU e nessuna unità SIMD (nemmeno MMX).
Sì abbiamo visto! Infatti è un "problema" che andrà in futuro affrontato probabilmente come tutti i compilatori dovremo usare dei profili per attivare diversi path d'istruzione a seconda della CPU / set di flags selezionati.
A me piacerebbe che Cosmos riuscisse a farlo in automatico magari interrogando alla partenza la CPU per vedere quale istruzioni supporta e poi con qualche symbol table chiamare una funzione piuttosto che un'altra, ma visto che l'assembler dobbiamo crearlo a compile time beh è un po' difficile farlo: andrebbero lasciati dei "buchi" nell'assembler stesso!
Con Edison ti va molto meglio, perché l'ISA Silvermont, e dunque fino alle SSE4.2. Ma niente AVX, purtroppo. Solo che non so se specificamente per Edison sia disponibile anche la modalità a 64 bit.
Tanto AVX per il momento non lo supportiamo non abbiamo ancora pensato a come risolvere il problema della FPU (e altre specifiche istruzioni che una CPU potrebbe avere, ma altre no), il nostro minimo comun denominatore è x86, 32 Bit con supporto a SSE2 quindi se ricordo bene si va indietro fino al Pentium III / 2000.
Abbiamo valutato che la compatibilità con i processori troppo vecchi non avesse alcun senso per un OS del 2016 inoltrato!
Chiaro. Quindi dovrà girare anche sul 6510 del Commodore64. :D
Temo sia un po' difficile che l'intero runtime .NET possa stare in 64 KByte di RAM e che poi ci sia spazio per il kernel e le applicazione. Magari qualcuno per "gioco" farà il porting per Amiga o Atari ST :Prrr:
Non credo che .NET la supporti. AFAIK è solo disponibile per Linux.
Ovviamente nessuno t'impedisce di realizzare un backend .NET per x32, magari partendo da quello x64.
E` sicuramente possibile! Per essere chiari dentro Cosmos non gira lo stesso .NET che gira su Windows, ma una versione nostra che viene compila in codice nativo un po' come .Net Native / Core Rt quindi noi per supportare x32 dovremmo "semplicemente" mettere il processore in long mode e quindi avremmo più registri, l'aritmetica coi long "ver", ma la dimensione di IntPtr / nativeInt sarebbe sempre di 32 bit. In realtà buona parte di .NET è riutilizzato, ma ci sono parti in cui .NET chiama C++ o peggio assembler e quelle parti vanno riscritte in C# usando quellli che in Cosmos sono chiamati plug. Per esempio la classe Console è tutta "pluggata" visto che noi scriviamo direttamente sulla Console VGA!
Esattamente. Ma con un runtime .NET il discorso cambia completamente, e il supporto sarebbe trasparente.
Infatti è questa la forza di .NET!
Non capisco perché la Microsoft non abbia fatto la mossa coraggiosa per Windows 10 e non sia ancora passata a .NET per tutto. Temo che il vero motivo sia il solito i 30 anni di compatibilità con X86!
@GTKM
Magari! Saresti solo che il benvenuto.
Quindi questa è la domanda da un milione di Euro: come può un'addizione non avere effetto? L'unico caso in matematica è quando l'addendo vale 0, ma qui valeva 0x5F800000!
Forse è perché la constante è un double mentre in FP0 c'è un long double? Vede solo gli ultimi 5 zeri?
Se la FADD viene eseguita ma nella destinazione il valore non cambia, allora sì, credo sia perché uno degli addendi è 0x0000.
Sì abbiamo visto! Infatti è un "problema" che andrà in futuro affrontato probabilmente come tutti i compilatori dovremo usare dei profili per attivare diversi path d'istruzione a seconda della CPU / set di flags selezionati.
A me piacerebbe che Cosmos riuscisse a farlo in automatico magari interrogando alla partenza la CPU per vedere quale istruzioni supporta e poi con qualche symbol table chiamare una funzione piuttosto che un'altra, ma visto che l'assembler dobbiamo crearlo a compile time beh è un po' difficile farlo: andrebbero lasciati dei "buchi" nell'assembler stesso!
Forse non ho capito. Essendo tutto compilato, potete solo scegliere quale blocco di codice eseguire in base alle istruzioni supportate dalla CPU, no? Ma l'assembly deve già essere stato generato in fase di compilazione.
Tanto AVX per il momento non lo supportiamo non abbiamo ancora pensato a come risolvere il problema della FPU (e altre specifiche istruzioni che una CPU potrebbe avere, ma altre no), il nostro minimo comun denominatore è x86, 32 Bit con supporto a SSE2 quindi se ricordo bene si va indietro fino al Pentium III / 2000.
Abbiamo valutato che la compatibilità con i processori troppo vecchi non avesse alcun senso per un OS del 2016 inoltrato!
Secondo me bisogna guardare semplicemente a quali sono le CPU più presenti nel mercato embedded oggi. Quindi credo che andare ancora più indietro nel tempo non abbia molto senso. Per il resto, essendo open source, se uno ha voglia... :D 
Temo sia un po' difficile che l'intero runtime .NET possa stare in 64 KByte di RAM e che poi ci sia spazio per il kernel e le applicazione. Magari qualcuno per "gioco" farà il porting per Amiga o Atari ST :Prrr:
Ma serve l'intero runtime, oppure qualcosa potrebbe essere tagliata via?
Infatti è questa la forza di .NET!
Non capisco perché la Microsoft non abbia fatto la mossa coraggiosa per Windows 10 e non sia ancora passata a .NET per tutto. Temo che il vero motivo sia il solito i 30 anni di compatibilità con X86!
Beh ovvio. Sicuramente dover mantenere la compatibilità ha avuto la sua influenza. D'altronde, un OS serve a far girare gli applicative degli utenti. :D
Se la FADD viene eseguita ma nella destinazione il valore non cambia, allora sì, credo sia perché uno degli addendi è 0x0000.
Eppure non lo è! Certo essendo passato come costante non lo vedo dentro alcun registro, ma l'istruzione fadd sembra corretta (beh in realtà ho qualche dubbio sul fatto che la costante sia tra parentesi quadre quindi vista come puntatore, ma per la versione con gli uint la Microsoft faceva così e funzionava quindi...).
Forse non ho capito. Essendo tutto compilato, potete solo scegliere quale blocco di codice eseguire in base alle istruzioni supportate dalla CPU, no? Ma l'assembly deve già essere stato generato in fase di compilazione.
Sì il fatto è che NON puoi basarti sul set d'istruzioni della CPU su cui stai compilando altrimenti otterresti un kernel che girerebbe solo sulla tua macchina.
GCC risolve il problema nel modo più banale di default compila per 486 (quindi solo FPU legacy, niente MMX, ...) e poi se uno lo richiede aggiungendo opportunamente dei flag nella linea di comando puoi usare mmx, sse1, see2, ...
Noi per fare in questo modo faremmo dei profili in Visual Studio tutti carucci però a me piacerebbe una soluzione più "automatica" la mia idea non sarebbe troppo lontana da avere un JIT solo per alcun istruzioni, ma non farlo tutte le volte che sono incontrate, ma solo alla partenza del Kernel interrogando la CPU stessa con CPUID.
Mi hanno che sarebbe possibile farlo usando indirect call, ma sarebbe molto più lento che avere l'assembler già pronto.
Per ora comunque il target è farlo girare su un normale x86 dal Pentium III in poi...
Secondo me bisogna guardare semplicemente a quali sono le CPU più presenti nel mercato embedded oggi. Quindi credo che andare ancora più indietro nel tempo non abbia molto senso. Per il resto, essendo open source, se uno ha voglia... :D 
No credo anch'io non abbia molto senso andare indietro fino al 486 o al Geode.
Ma serve l'intero runtime, oppure qualcosa potrebbe essere tagliata via?
Se inizi a tagliare via dei pezzi dal runtime poi non funziona più il discorso "compile once, run everywhere". L'immagine ISO di un kernel Cosmos di test è di 16 MB e ti faccio notare che:
 siamo in modalità debug quindi ci sono anche tutti i simboli di debug dentro l'immagine
 Il compilatore, appunto perché siamo in fase di test non ottimizza l'assembler
Beh ovvio. Sicuramente dover mantenere la compatibilità ha avuto la sua influenza. D'altronde, un OS serve a far girare gli applicative degli utenti. :D
Concordo! Però sarebbe bello dopo 30 anni avere qualcosa di nuovo, ma se viene rigettato solo perché non ci girano le applicazioni x86 (come nel caso di Itanium e Windows Rt) beh Midori resterà sempre e solo nei laboratori di ricerca Microsoft  .
* In un OS language based come Midori e Cosmos codice "nativo" non può / non deve girare se no cadrebbe lo scopo per cui è stato scritto. Solo applicazioni .NET che vengono trasformate in assembler ad installation time... quindi no niente Firefox (!), Python sì lo si potrà avere nella forma di Iron Python però...
Eppure non lo è! Certo essendo passato come costante non lo vedo dentro alcun registro, ma l'istruzione fadd sembra corretta (beh in realtà ho qualche dubbio sul fatto che la costante sia tra parentesi quadre quindi vista come puntatore, ma per la versione con gli uint la Microsoft faceva così e funzionava quindi...).
Però, matematica alla mano... :stordita: 
Sì il fatto è che NON puoi basarti sul set d'istruzioni della CPU su cui stai compilando altrimenti otterresti un kernel che girerebbe solo sulla tua macchina.
GCC risolve il problema nel modo più banale di default compila per 486 (quindi solo FPU legacy, niente MMX, ...) e poi se uno lo richiede aggiungendo opportunamente dei flag nella linea di comando puoi usare mmx, sse1, see2, ...
Noi per fare in questo modo faremmo dei profili in Visual Studio tutti carucci però a me piacerebbe una soluzione più "automatica" la mia idea non sarebbe troppo lontana da avere un JIT solo per alcun istruzioni, ma non farlo tutte le volte che sono incontrate, ma solo alla partenza del Kernel interrogando la CPU stessa con CPUID.
Però questo significa che se io volessi un programma, dovrei prima installare Visual Studio e compilarlo "per Cosmos"?
Secondo me è poco elastica come soluzione, sinceramente.
Se inizi a tagliare via dei pezzi dal runtime poi non funziona più il discorso "compile once, run everywhere". L'immagine ISO di un kernel Cosmos di test è di 16 MB e ti faccio notare che:
 siamo in modalità debug quindi ci sono anche tutti i simboli di debug dentro l'immagine
 Il compilatore, appunto perché siamo in fase di test non ottimizza l'assembler
Intendevo un'altra cosa: il .NET è enorme. Se pensi ad un OS per il mercato embedded, serve davvero tutto? La mia è una vera curiosità, non una domanda retorica eh :D 
Concordo! Però sarebbe bello dopo 30 anni avere qualcosa di nuovo, ma se viene rigettato solo perché non ci girano le applicazioni x86 (come nel caso di Itanium e Windows Rt) beh Midori resterà sempre e solo nei laboratori di ricerca Microsoft  .
* In un OS language based come Midori e Cosmos codice "nativo" non può / non deve girare se no cadrebbe lo scopo per cui è stato scritto. Solo applicazioni .NET che vengono trasformate in assembler ad installation time... quindi no niente Firefox (!), Python sì lo si potrà avere nella forma di Iron Python però...
Eh lo so, però un'azienda come Microsoft campa(va) su quello, quindi non è che potesse tagliare decennia di compatibilità Fosse stato possibile, sono sicuro che I loro dev sarebbero stati i primi ad esultare. :D
Riguardo il codice "nativo", beh, in un certo senso è come se il codice scritto in C# lo sia già, per Cosmos, no?
Però, matematica alla mano... :stordita: 
Già, ma è impossibile però! Deve essere qualche errore stupido e banali di quelli che rileggi il codice mille volte e ti pare giusto poi arriva il collega che non centra una fava e bam...
Però questo significa che se io volessi un programma, dovrei prima installare Visual Studio e compilarlo "per Cosmos"?
Secondo me è poco elastica come soluzione, sinceramente.
Beh un'applicazione C# per compilarla devi sempre usare Visual Studio (o Mono Develop se vuoi fare l'esotico), comunque no a parte i driver o parti del kernel tu creerai un normale .exe che girerà su Windows, Linux e Cosmos.
Semplicemente su Cosmos durante l'installazione / prima esecuzione viene compilato in assembler un po' come fa NGEN. Credo sia addirittura possibile mettere il codice binario dentro l'eseguibile stesso. Le volte successive è il codice binario che verrà eseguito!
Intendevo un'altra cosa: il .NET è enorme. Se pensi ad un OS per il mercato embedded, serve davvero tutto? La mia è una vera curiosità, non una domanda retorica eh :D 
Beh dai 16 MB non sono "enormi", OK per ora manca ancora qualcosa ma non credo crescerà poi molto di più!
Vedi in teoria è molto bello avere un OS che occupa pochi MB peccato che poi ti trovi busybox che fa schifo, scopri che la libinconv "ritagliata" non supporta la codepage che ti serve e quindi sei sempre a ricompilarla, che l'UTF-8 "ritagliato" in realtà da errore se vede multi byte characters...
Secondo me dentro .NET c'è il minimo necessario per avere un OS / runtime che ha senso.
Eh lo so, però un'azienda come Microsoft campa(va) su quello, quindi non è che potesse tagliare decennia di compatibilità Fosse stato possibile, sono sicuro che I loro dev sarebbero stati i primi ad esultare. :D
Riguardo il codice "nativo", beh, in un certo senso è come se il codice scritto in C# lo sia già, per Cosmos, no?
In realtà il codice "nativo" di Cosmos è IL non C#, infatti se ti piace puoi usare anche VB.NET o F#... anche C++/CLI se ti auto-limiti con /clr-pure.
C/C++ o assembler sono vietate perché:
 Non sono portabili
 Sono unsafe by design
 X# stesso non sarà usabile a livello utente: non è verificabile
Già, ma è impossibile però! Deve essere qualche errore stupido e banali di quelli che rileggi il codice mille volte e ti pare giusto poi arriva il collega che non centra una fava e bam...
Probabilmente... :D 
Beh un'applicazione C# per compilarla devi sempre usare Visual Studio (o Mono Develop se vuoi fare l'esotico), comunque no a parte i driver o parti del kernel tu creerai un normale .exe che girerà su Windows, Linux e Cosmos.
Semplicemente su Cosmos durante l'installazione / prima esecuzione viene compilato in assembler un po' come fa NGEN. Credo sia addirittura possibile mettere il codice binario dentro l'eseguibile stesso. Le volte successive è il codice binario che verrà eseguito!
Quindi, durante l'installazione su Cosmos si avrà la fase di assembling? Scusami, è solo per avere le idee chiare.
Beh dai 16 MB non sono "enormi", OK per ora manca ancora qualcosa ma non credo crescerà poi molto di più!
Vedi in teoria è molto bello avere un OS che occupa pochi MB peccato che poi ti trovi busybox che fa schifo, scopri che la libinconv "ritagliata" non supporta la codepage che ti serve e quindi sei sempre a ricompilarla, che l'UTF-8 "ritagliato" in realtà da errore se vede multi byte characters...
Secondo me dentro .NET c'è il minimo necessario per avere un OS / runtime che ha senso.
Probabile, io intendevo chiedere appunto se fosse possibile "rimuovere" parti del .NET che non dovrebbero aver motivo di esistere in un OS per sistemi embedded. Ma credo che abbiate già ridotto tutto all'osso. :D
In realtà il codice "nativo" di Cosmos è IL non C#, infatti se ti piace puoi usare anche VB.NET o F#... anche C++/CLI se ti auto-limiti con /clr-pure.
C/C++ o assembler sono vietate perché:
 Non sono portabili
 Sono unsafe by design
 X# stesso non sarà usabile a livello utente: non è verificabile
In teoria, in un sistema così pensato, un software scritto in C# su Cosmos avrebbe prestazioni paragonabili ad uno scritto in C/C++ su altri OS, giusto?
Probabilmente... :D 
Sono in chat con Matthijs ter Woord, il nostro Linus Torvald, solo che è molto più educato: non ha ancora mandato a fare in c*lo nessuno!
E mi chiedeva cosa ci fanno le [ ] davanti alla costante della fadd? In effetti non sono sicuro nemmeno io che abbia senso, ma il codice MASM originale la passava come puntatore 'ptr .label'... può essere questo il problema... magari Bochs è "educato" e anche se punto a RAM  a caso mi trovo tutti 0?
Quindi, durante l'installazione su Cosmos si avrà la fase di assembling? Scusami, è solo per avere le idee chiare.
Qui parliamo del futuro, molto è ancora da decidere. Il Kernel è trasformato in assembler durante la compilazione, ma le altre applicazioni che NON sono integrate dentro al kernel dovranno essere compilate durante l'installazione.
Nota che per ora Cosmos è super monolitico non abbiamo ancora il supporto per i thread (processi in altri OS) quindi per ora le "applicazioni" sono chiamate a funzione dentro la shell!
Probabile, io intendevo chiedere appunto se fosse possibile "rimuovere" parti del .NET che non dovrebbero aver motivo di esistere in un OS per sistemi embedded. Ma credo che abbiate già ridotto tutto all'osso. :D
In realtà quello che manca è perché non l'abbiamo implementato! Ci sarà tutto probabilmente ma sarà più piccolo che il runtime installato su Windows lo stesso: io sono assolutamente convinto che sia Windows che Linux mettano "garbage" dentro gli eseguibili per giustificare HDD più grandi :p
In teoria, in un sistema così pensato, un software scritto in C# su Cosmos avrebbe prestazioni paragonabili ad uno scritto in C/C++ su altri OS, giusto?
Esatto:
joeduffyblog.com/2015/12/19/safe-native-code/
Dopo aver hexpumpato (?) il long double -1.0 dal C e aver ottenuto questo schifo:
sizeof(long double) 12
-1 as long double '0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x80 0xff 0xbf 0x00 0x00 '
sizeof(double) 8
-1 as double '0x00 0x00 0x00 0x00 0x00 0x00 0xf0 0xbf '
mentre BOCHS mi mostrava sta roba:
FP7 ST0(v): raw 0xbfff:8000000000000000 (-1,0000000000) (NORMAL)
I byte sono in posizioni diverse (endianess?) ma sembra il valore che vedo nel long double quindi l'FPU è a 80 Bit!
Quindi la costante è errata ed è per questo che non va una mazza?
E soprattutto è giusto avere l'FPU a 80 Bit o è meglio averla settata come 64 bit (così da evitare continue conversioni?).
Anche la Microsoft non sembra convinta:
    4.1.6 Floating-point types
    Floating-point operations may be performed with higher precision than the result type of the operation. For example, some hardware architectures support an “extended” or “long double” floating-point type with greater range and precision than the double type, and implicitly perform all floating-point operations using this higher precision type. Only at excessive cost in performance can such hardware architectures be made to perform floating-point operations with less precision, and rather than require an implementation to forfeit both performance and precision, C# allows a higher precision type to be used for all floating-point operations. Other than delivering more precise results, this rarely has any measurable effects. However, in expressions of the form x * y / z, where the multiplication produces a result that is outside the double range, but the subsequent division brings the temporary result back into the double range, the fact that the expression is evaluated in a higher range format may cause a finite result to be produced instead of an infinity. To force a value of a floating point type to the exact precision of its type, an explicit cast can be used.
Perché questo? "Only at excessive cost in performance can such hardware architectures be made to perform floating-point operations with less precision"
non posso settare la FPU a 64 Bit alla partenza e dimenticarmene? Sarebbe più lenta? Padda gli altri 4 byte con 0 ogni volta?
A questo solo il nostro insider Intel può rispondere mi sa :p
cdimauro
27-06-2016, 22:29
Ma sapete che 'sto Cosmos mi sta intrippando? Quasi quasi, appena mi libero un po' con la tesi (Aaaaaah, le catene di Markov... :fagiano: ) mi unisco a voi :D
Fai bene: è un ottimo progetto, innovativo, estremamente intrigante, e formativo.
Ieri sera abbiamo messo Bochs in modalità debug e siamo andati a vedere step per step cosa c'era nei registri vi faccio un breve riassunto:
 Nello stack c'era l'unlong FFF... diviso in 2 parti essendo - ovviamente - lo stack x86 composto da parti a 32 Bit
 Dopo la mov in EAX c'era la parte alta del nosto ulong (0xFFFF)
 Dentro il registo ST0 della CPU era inserito -1.0 ma in esadecimale non era il valore che mi aspettavo (0xBFFF invece di 0xF0BF) forse perchè la FPU era in extended precision? Gli strani floating point a 80 bit...
 Il jump era correttamente non eseguito perchè EAX aveva il segno (!)
 Ma la fadd non faceva assolutamente nulla e quindi dopo quell'istruzione in ST0 c'era ancora -1.0 / 0xBFFF!
 fstp convertiva correttamente 0xBFFF in 0xF0BF e lo copiava nello stack
L'FPU x87 dovrebbe funzionare internamente sempre a 80 bit, garantendo la massima precisione, a prescindere da come vengano caricati i dati nei registri.
Adesso non ho tempo né testa (il post-partita è, diciamo, un po' traumatico :ubriachi: ), ma se ti serve appena posso indagherò e ti farò sapere (non prima di domani sera, comunque).
Riguardo alla costante caricata nello stack e nel registro ST0, sei sicuro che Bochs ti mostri una rappresentazione corretta?
Quindi questa è la domanda da un milione di Euro: come può un'addizione non avere effetto? L'unico caso in matematica è quando l'addendo vale 0, ma qui valeva 0x5F800000!
Forse è perché la constante è un double mentre in FP0 c'è un long double? Vede solo gli ultimi 5 zeri?
No, come ti dicevo sopra, una volta caricato il dato, l'FPU lavora a precisione massima (non posso darti la sicurezza al 100% perché dovrei controllare come già detto, ma io ricordo così al momento).
La causa dev'essere un'altra.
Beh non è che ci lavoro solo io eh! Non sono nemmeno il capo del team io mi sono aggiunto solo di recente (volevo il cursore quadrato e blikante tipo C=64 e poi son finito a scrivere assembler in C# :p).
Tranquillo: un giorno ci tornerai, e lo farai girare anche sul C64. :D
Sì abbiamo visto! Infatti è un "problema" che andrà in futuro affrontato probabilmente come tutti i compilatori dovremo usare dei profili per attivare diversi path d'istruzione a seconda della CPU / set di flags selezionati.
A me piacerebbe che Cosmos riuscisse a farlo in automatico magari interrogando alla partenza la CPU per vedere quale istruzioni supporta e poi con qualche symbol table chiamare una funzione piuttosto che un'altra, ma visto che l'assembler dobbiamo crearlo a compile time beh è un po' difficile farlo: andrebbero lasciati dei "buchi" nell'assembler stesso!
Ma non avete una gerarchia di classi per gestire il tutto? Istanzi le classi giuste all'inizio, che si occupano di generare codice binario adeguato all'architettura corrente, e siete a posto. L'OOP si presta bene per modellare questo tipo di problemi, eliminando buona parte degli odiosi (e poco performanti) if-then-else.
Tanto AVX per il momento non lo supportiamo non abbiamo ancora pensato a come risolvere il problema della FPU (e altre specifiche istruzioni che una CPU potrebbe avere, ma altre no), il nostro minimo comun denominatore è x86, 32 Bit con supporto a SSE2 quindi se ricordo bene si va indietro fino al Pentium III / 2000.
No, per le SSE2 devi avere almeno un Pentium 4. Le SSE sono arrivate col Pentium 3.
Comunque non vi fossilizzate con processore = versione minima per avere alcune estensioni, perché un processo moderno può benissimo implementare una vecchia ISA per particolari esigenze, come dimostra Galileo.
Abbiamo valutato che la compatibilità con i processori troppo vecchi non avesse alcun senso per un OS del 2016 inoltrato!
Vedi sopra: ci sono processi moderni che supportano vecchie ISA!
Non vi sbarazzerete così facilmente del legacy. :D
E comunque, dati i vostri obiettivi, è giusto che sia così.
Temo sia un po' difficile che l'intero runtime .NET possa stare in 64 KByte di RAM e che poi ci sia spazio per il kernel e le applicazione. Magari qualcuno per "gioco" farà il porting per Amiga o Atari ST :Prrr:
Ci sono delle schede acceleratrici nuove di pacca per Amiga che offrono 128MB di memoria DDR3, e prestazioni 200 volte superiori a un Amiga 600, per cui... si può fare. :) Ma non posso/voglio mettermici io.
E` sicuramente possibile! Per essere chiari dentro Cosmos non gira lo stesso .NET che gira su Windows, ma una versione nostra che viene compila in codice nativo un po' come .Net Native / Core Rt quindi noi per supportare x32 dovremmo "semplicemente" mettere il processore in long mode e quindi avremmo più registri, l'aritmetica coi long "ver", ma la dimensione di IntPtr / nativeInt sarebbe sempre di 32 bit.
Esattamente. L'unica "oddity" è rappresentata dai puntatori a 32-bit pur avendo gli interi a 64 bit "nativi", ma va bene così: tanto si usano per lo più gli interi a 32-bit, mentre i 64-bit si possono strategicamente usare per spostamenti/copie di memoria, azzeramento, ricerche, ecc.
In realtà buona parte di .NET è riutilizzato, ma ci sono parti in cui .NET chiama C++ o peggio assembler e quelle parti vanno riscritte in C# usando quellli che in Cosmos sono chiamati plug. Per esempio la classe Console è tutta "pluggata" visto che noi scriviamo direttamente sulla Console VGA!
Strano che ci sia tanta roba in C++ o assembly: pensavo che ci fosse soltanto una piccola parte, e il resto gestito dal backend che emette il codice binario specifico.
Infatti è questa la forza di .NET!
Non capisco perché la Microsoft non abbia fatto la mossa coraggiosa per Windows 10 e non sia ancora passata a .NET per tutto. Temo che il vero motivo sia il solito i 30 anni di compatibilità con X86!
Non credo. Probabilmente è perché con .NET non vai sempre meglio di C++ a livello di startup e/o prestazioni e/o dimensione del codice e/o consumo della memoria, e comunque non hai la flessibilità che ti offre C++ nel gestire a tuo assoluto piacimento la memoria.
Comunque C# è un gran, ottimo compromesso, e per quanto mi riguarda fate benissimo a portare avanti Cosmos
Eppure non lo è! Certo essendo passato come costante non lo vedo dentro alcun registro, ma l'istruzione fadd sembra corretta (beh in realtà ho qualche dubbio sul fatto che la costante sia tra parentesi quadre quindi vista come puntatore, ma per la versione con gli uint la Microsoft faceva così e funzionava quindi...).
Scusami, ma non avete una test suite da lanciare per verificare che quel che fate funzioni, senza dover tirar sù Bochs ogni volta?
Altrimenti così perdete troppo tempo andando a caccia di bug come questi.
Concordo! Però sarebbe bello dopo 30 anni avere qualcosa di nuovo, ma se viene rigettato solo perché non ci girano le applicazioni x86 (come nel caso di Itanium e Windows Rt) beh Midori resterà sempre e solo nei laboratori di ricerca Microsoft  .
Potreste realizzare un JIT-er che esegue le vecchie applicazioni x86, convertendole però in IL, fino a mappare man mano tutto il codice in IL.
Dopodiché potreste ricompilare l'applicazione x86 in qualunque architettura. ;)
* In un OS language based come Midori e Cosmos codice "nativo" non può / non deve girare se no cadrebbe lo scopo per cui è stato scritto. Solo applicazioni .NET che vengono trasformate in assembler ad installation time... quindi no niente Firefox (!), Python sì lo si potrà avere nella forma di Iron Python però...
Hai detto niente. :cool: Va bene così. ;)
E mi chiedeva cosa ci fanno le [ ] davanti alla costante della fadd? In effetti non sono sicuro nemmeno io che abbia senso, ma il codice MASM originale la passava come puntatore 'ptr .label'... può essere questo il problema... magari Bochs è "educato" e anche se punto a RAM  a caso mi trovo tutti 0?
E' possibile che punti a una zona di memoria che genera eccezioni, ma provoca i problemi di cui hai discusso finora.
Esatto:
joeduffyblog.com/2015/12/19/safe-native-code/
TL;Read Later. :fagiano:
Dopo aver hexpumpato (?) il long double -1.0 dal C e aver ottenuto questo schifo:
sizeof(long double) 12
-1 as long double '0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x80 0xff 0xbf 0x00 0x00 '
sizeof(double) 8
-1 as double '0x00 0x00 0x00 0x00 0x00 0x00 0xf0 0xbf '
Mi pare normale: il primo è un extended a 80 bit, e il secondo il classico double a 64 bit.
mentre BOCHS mi mostrava sta roba:
FP7 ST0(v): raw 0xbfff:8000000000000000 (-1,0000000000) (NORMAL)
Strano. :(
I byte sono in posizioni diverse (endianess?) ma sembra il valore che vedo nel long double quindi l'FPU è a 80 Bit!
Sì, certo. Dovrebbe essere sempre così, internamente (ma puoi anche memorizzare un extended a 80 bit in memoria).
Quindi la costante è errata ed è per questo che non va una mazza?
E soprattutto è giusto avere l'FPU a 80 Bit o è meglio averla settata come 64 bit (così da evitare continue conversioni?).
E' giusto così, ma al momento non capisco cos'è che non ti faccia funzionare il codice.
Anche la Microsoft non sembra convinta:
Perché questo? "Only at excessive cost in performance can such hardware architectures be made to perform floating-point operations with less precision"
non posso settare la FPU a 64 Bit alla partenza e dimenticarmene? Sarebbe più lenta? Padda gli altri 4 byte con 0 ogni volta?
L'FPU (x87) non è più lenta: funziona così.
Però mi pare che abbia delle istruzioni di FADD e FMUL che vengono eseguite a precisione inferiore a quella estesa, ma che sono più veloci. Solo che al momento non posso verificare.
A questo solo il nostro insider Intel può rispondere mi sa :p
Spero che ti sia stato utile. Io vado a smaltire adesso. :hic:
EDIT: per parlare in generale di Cosmos, ti consiglio di aprire un apposito thread. ;)
Fai bene: è un ottimo progetto, innovativo, estremamente intrigante, e formativo.
La penso anch'io così anche se Cosmos non diventerà mai il Linux del futuro (ma accadrà :D) non sarebbe comunque tempo perso!
L'FPU x87 dovrebbe funzionare internamente sempre a 80 bit, garantendo la massima precisione, a prescindere da come vengano caricati i dati nei registri.
Ma non è possibile settarla a 64 Bit? Tutte queste conversioni tra double <--> long double anche se fatte in hardware un costo lo avranno, no?
Adesso non ho tempo né testa (il post-partita è, diciamo, un po' traumatico :ubriachi: ), ma se ti serve appena posso indagherò e ti farò sapere (non prima di domani sera, comunque).
Te ne sarei grato sta cosa dell'addizione che non addiziona mi sta facendo impazzire :D
Riguardo alla costante caricata nello stack e nel registro ST0, sei sicuro che Bochs ti mostri una rappresentazione corretta?
Boh se mostra pure roba sbagliata siamo fritti! Già l'interfaccia fa alquanto schifo ci manca pure che mostri una rappresentazione errata :doh: 
No, come ti dicevo sopra, una volta caricato il dato, l'FPU lavora a precisione massima (non posso darti la sicurezza al 100% perché dovrei controllare come già detto, ma io ricordo così al momento).
La causa dev'essere un'altra.
Sicuramente è qualche errore stupido...
Tranquillo: un giorno ci tornerai, e lo farai girare anche sul C64. :D
Beh il mio OS ha già la shell con lo sfondo blu e le scritte bianche, con "Ready" e il cursore che lampeggia quindi :p
Se in futuro saprà in di eseguire script in C# o meglio ancora VB.NET potrò dire di aver riprodotto l'esperienza quasi totalmente :p
Ma non avete una gerarchia di classi per gestire il tutto? Istanzi le classi giuste all'inizio, che si occupano di generare codice binario adeguato all'architettura corrente, e siete a posto. L'OOP si presta bene per modellare questo tipo di problemi, eliminando buona parte degli odiosi (e poco performanti) if-then-else.
Beh abbiamo le classi questa per esempio è la classe Add.cs che converte l'IL in Assembler: https://github.com/fanoI/Cosmos/blob/master/source/Cosmos.IL2CPU/IL/Add.cs.
Come puoi vedere nel caso di float / double sono ora usate le istruzione SSE (prima venivano usate solo per i float e a me questo ha rotto l'armonia :D), IL2CPU è letteralmente il nostro compilatore quindi non possiamo sapere quale sarà l'architettura sulla quale andremo a girare. Certo quando metteremo su questa idea dei profili non voglio che si riempa tutto di if (FPU.isX87), if (FPU.isSSE). I mie prossimi passi (dopo che conv.r.un funziona se no, non me lo merito :D):
 C'è ancora qualcosa da aggiustare nel backend X#: alcune istruzioni non sono generabile senza usa Xs.Literal() altre potrebbero essere fatte meglio
 Pensavo di creare una classe astratta / interfaccia FPU in cui mettere funzioni tipo Init(),  CVT2Int(), CVT2Long(), Add()... e uno deriva da quella classe l'implementazione per FPU e SSE.
Per esempio un ipotetica SSE.Add() diventerebbe:
static void Add()
      XS.SSE2.MoveSD(XMM0, ESP, sourceIsIndirect: true);
      // Move the stack of 8 bytes to get the second double
      XS.Add(ESP, 8);
      XS.SSE2.MoveSD(XMM1, ESP, sourceIsIndirect: true);
      XS.SSE2.AddSD(XMM1, XMM0);
      XS.SSE2.MoveSD(ESP, XMM1, destinationIsIndirect: true);{
}
No, per le SSE2 devi avere almeno un Pentium 4. Le SSE sono arrivate col Pentium 3.
Comunque non vi fossilizzate con processore = versione minima per avere alcune estensioni, perché un processo moderno può benissimo implementare una vecchia ISA per particolari esigenze, come dimostra Galileo.
Vedi sopra: ci sono processi moderni che supportano vecchie ISA!
Non vi sbarazzerete così facilmente del legacy. :D
E comunque, dati i vostri obiettivi, è giusto che sia così.
Già!
Ci sono delle schede acceleratrici nuove di pacca per Amiga che offrono 128MB di memoria DDR3, e prestazioni 200 volte superiori a un Amiga 600, per cui... si può fare. :) Ma non posso/voglio mettermici io.
Chissà qualche "matto" poi lo si trova che fa il porting per Amiga.
Esattamente. L'unica "oddity" è rappresentata dai puntatori a 32-bit pur avendo gli interi a 64 bit "nativi", ma va bene così: tanto si usano per lo più gli interi a 32-bit, mentre i 64-bit si possono strategicamente usare per spostamenti/copie di memoria, azzeramento, ricerche, ecc.
Infatti! 32x avrebbe senso ma per ora è prematuro pensarci...
Strano che ci sia tanta roba in C++ o assembly: pensavo che ci fosse soltanto una piccola parte, e il resto gestito dal backend che emette il codice binario specifico.
Guarda ha sorpreso anche me, ma è così C# almeno la versione standard per Windows fa chiamate a moltissime librerie di Windows che sono, ovviamente, scritte in C++.
Anche la classe String è piena di P/Invoke!
Cosmos è nato prima che la Microsoft iniziasse a mettere .NET su GitHub e pensasse seriamente a renderlo portabile, probabilmente usare Core.Rt semplificherebbe le cose, ma vorrebbe dire ricominciare da capo!
Non credo. Probabilmente è perché con .NET non vai sempre meglio di C++ a livello di startup e/o prestazioni e/o dimensione del codice e/o consumo della memoria, e comunque non hai la flessibilità che ti offre C++ nel gestire a tuo assoluto piacimento la memoria.
Il C# usato per scrivere Midori questi problemi in realtà non li avrebbe avuti anche se alla fine mi chiedo quanto fosse "davvero" C#... 
Comunque C# è un gran, ottimo compromesso, e per quanto mi riguarda fate benissimo a portare avanti Cosmos
Sì la Microsoft nel copiare e migliorare Java ha fatto davvero un ottimo lavoro!
Scusami, ma non avete una test suite da lanciare per verificare che quel che fate funzioni, senza dover tirar sù Bochs ogni volta?
Beh stiamo scrivendo un Kernel quindi il nostro test è lanciare un Sistema Operativo: l'IL viene compilato in assembler con IL2CPU, un'immagine ISO è creata e messa in bocca in Bochs!
Il boot è molto veloce (ed è integrato nel tasto "run" di Visual Studio) forse potrebbe aver senso scrivere il codice NASM e compilarlo "altrove", ma non sono poi come stampare il valore ottenuto :p
Potreste realizzare un JIT-er che esegue le vecchie applicazioni x86, convertendole però in IL, fino a mappare man mano tutto il codice in IL.
Dopodiché potreste ricompilare l'applicazione x86 in qualunque architettura. ;)
Temo sarebbe troppo lento, e poi incoraggerebbe la gente a NON scrivere codice "nativo" per Cosmos quindi...
Hai detto niente. :cool: Va bene così. ;)
Vedi io seguivo Haiku (clone di BeOS) ed era un bel progetto ben impostato, con un kernel scritto in C++ ecc...
Poi venne il "problema" di come portare applicazioni Open Source su Haiku la cui alberatura delle directory rispetto Unix / Linux aveva pure senso,  prendendo ad esempio sempre Pyton vuole scrivere la configurazione in /etc mentre in Haiku andrebbe in /Sytem/cfg...
Beh invece di fare il porting "corretto" hanno creato un package manager (il diavolo!) con un filesystem virtuale in modo che Python creda di scrivere in /etc mentre invece scrive su una specie di "immagine". Follia pura!
Sono passati 3 anni e sono ancora lì a smenarsela col package manager e peggio ancora hanno perso la retro-compatibilità con BeOS: l'obiettivo per cui Haiku fu fatto.
Per me meglio riscriversi tutto che farsi "imporre" le scelte di GNU...
Però mi pare che abbia delle istruzioni di FADD e FMUL che vengono eseguite a precisione inferiore a quella estesa, ma che sono più veloci. Solo che al momento non posso verificare.
Spero che ti sia stato utile. Io vado a smaltire adesso. :hic:
Grazie se le trovi mi fai un piacere!
EDIT: per parlare in generale di Cosmos, ti consiglio di aprire un apposito thread. ;)
Sì sono solo in dubbio se aprirlo in sezione Sistemi Operativi o qui in programmazione...
cdimauro
28-06-2016, 22:17
La penso anch'io così anche se Cosmos non diventerà mai il Linux del futuro (ma accadrà :D) non sarebbe comunque tempo perso!
Mai dire mai: se il progetto e buono, e visto che è basato su .NET & compagnia, potrebbe anche ritagliarsi un futuro. ;)
Ma non è possibile settarla a 64 Bit?
Sì:
"The precision-control (PC) field (bits 8 and 9 of the x87 FPU control word) determines the precision (64, 53, or 24 bits) of floating-point calculations made by the x87 FPU (see Table 8-2). The default precision is double extended precision, which uses the full 64-bit significand available with the double extended-precision floating-point format of the x87 FPU data registers. This setting is best suited for most applications, because it allows applications to take full advantage of the maximum precision available with the x87 FPU data registers.
The double precision and single precision settings reduce the size of the significand to 53 bits and 24 bits, respectively. These settings are provided to support IEEE Standard 754 and to provide compatibility with the specifications of certain existing programming languages. Using these settings nullifies the advantages of the double extended-precision floating-point format's 64-bit significand length. When reduced precision is specified, the rounding of the significand value clears the unused bits on the right to zeros."
Quindi butti soltanto via una cosa che è utilissima: lavorare a precisione più elevata di quanto fanno le altre FPU.
Tutte queste conversioni tra double <--> long double anche se fatte in hardware un costo lo avranno, no?
No, l'FPU impiega sempre le stesse risorse per elaborare le operazioni a qualunque precisione venga impostata. Si parla soltanto di troncamento dei risultati, e mai di prestazioni, com'è ovvio che sia.
Te ne sarei grato sta cosa dell'addizione che non addiziona mi sta facendo impazzire :D
Purtroppo anche rileggendo non mi pare ci sia nulla di sbagliato. Anche il dump esadecimale dei valori in extended e double mi sembrano corretti, perché usano un esponente diverso (più grande nel primo caso, ovviamente), da cui si spiegano 0xbfff80 (c'è un bit in più, quello che porta a 0x80 il terz'ultimo byte) per extended e 0xbff0 per il double.
Boh se mostra pure roba sbagliata siamo fritti! Già l'interfaccia fa alquanto schifo ci manca pure che mostri una rappresentazione errata :doh: 
I dump sembrano corretti.
Sicuramente è qualche errore stupido...
Ma hai provato a eseguire passo passo il codice generato e vedere quali valori vengono referenziati, e i risultati prodotti?
Beh abbiamo le classi questa per esempio è la classe Add.cs che converte l'IL in Assembler: https://github.com/fanoI/Cosmos/blob/master/source/Cosmos.IL2CPU/IL/Add.cs.
Come puoi vedere nel caso di float / double sono ora usate le istruzione SSE (prima venivano usate solo per i float e a me questo ha rotto l'armonia :D), IL2CPU è letteralmente il nostro compilatore quindi non possiamo sapere quale sarà l'architettura sulla quale andremo a girare. Certo quando metteremo su questa idea dei profili non voglio che si riempa tutto di if (FPU.isX87), if (FPU.isSSE). I mie prossimi passi (dopo che conv.r.un funziona se no, non me lo merito :D):
Bene, perché si sta generando troppo spaghetti code con tutti questi if e #if.
Vedrai quanto sarà elegante quando userete il polimorfismo per gestire i backend. :)
 C'è ancora qualcosa da aggiustare nel backend X#: alcune istruzioni non sono generabile senza usa Xs.Literal() altre potrebbero essere fatte meglio
 Pensavo di creare una classe astratta / interfaccia FPU in cui mettere funzioni tipo Init(),  CVT2Int(), CVT2Long(), Add()... e uno deriva da quella classe l'implementazione per FPU e SSE.
Per esempio un ipotetica SSE.Add() diventerebbe:
static void Add()
      XS.SSE2.MoveSD(XMM0, ESP, sourceIsIndirect: true);
      // Move the stack of 8 bytes to get the second double
      XS.Add(ESP, 8);
      XS.SSE2.MoveSD(XMM1, ESP, sourceIsIndirect: true);
      XS.SSE2.AddSD(XMM1, XMM0);
      XS.SSE2.MoveSD(ESP, XMM1, destinationIsIndirect: true);{
}
Esattamente, ma immagino che non abbiate nessun ottimizzatore al momento, perché il codice è abbastanza "gonfiato" da istruzioni che riproducono quello che farebbe un emulatore.
Chissà qualche "matto" poi lo si trova che fa il porting per Amiga.
Ormai ci sono pochi sviluppatori, e la comunità è pure spaccata in 4, giusto per sprecare ancora di più le poche risorse.
Guarda ha sorpreso anche me, ma è così C# almeno la versione standard per Windows fa chiamate a moltissime librerie di Windows che sono, ovviamente, scritte in C++.
Anche la classe String è piena di P/Invoke!
Cosmos è nato prima che la Microsoft iniziasse a mettere .NET su GitHub e pensasse seriamente a renderlo portabile, probabilmente usare Core.Rt semplificherebbe le cose, ma vorrebbe dire ricominciare da capo!
Quindi riscrivere tutto il C# non se ne parla. Peccato. In questo modo rimane una grossa dipendenza.
Il C# usato per scrivere Midori questi problemi in realtà non li avrebbe avuti anche se alla fine mi chiedo quanto fosse "davvero" C#... 
Quindi non è C# "puro"?
Beh stiamo scrivendo un Kernel quindi il nostro test è lanciare un Sistema Operativo: l'IL viene compilato in assembler con IL2CPU, un'immagine ISO è creata e messa in bocca in Bochs!
Il boot è molto veloce (ed è integrato nel tasto "run" di Visual Studio) forse potrebbe aver senso scrivere il codice NASM e compilarlo "altrove", ma non sono poi come stampare il valore ottenuto :p
Io parlavo di qualcosa di diverso: unit testing. Con tanti test che vengono esercitati sul codice che avete scritto.
Niente NASM dunque, visto che rimarrebbe tutto in C#, e con la possibilità di eseguire il codice generato dal vostro IL in una sandbox e leggerne i risultati.
Temo sarebbe troppo lento, e poi incoraggerebbe la gente a NON scrivere codice "nativo" per Cosmos quindi...
Lenta sarebbe soltanto la parte di mappatura del codice x86/x64, per generare l'IL finale. 
Comunque, sì: non avreste codice nativo Cosmos, ma d'altra parte Cosmos è basato su IL, e quest'ultimo potrebbe provenire da qualunque fonte. :fiufiu:
Vedi io seguivo Haiku (clone di BeOS) ed era un bel progetto ben impostato, con un kernel scritto in C++ ecc...
Poi venne il "problema" di come portare applicazioni Open Source su Haiku la cui alberatura delle directory rispetto Unix / Linux aveva pure senso,  prendendo ad esempio sempre Pyton vuole scrivere la configurazione in /etc mentre in Haiku andrebbe in /Sytem/cfg...
Beh invece di fare il porting "corretto" hanno creato un package manager (il diavolo!) con un filesystem virtuale in modo che Python creda di scrivere in /etc mentre invece scrive su una specie di "immagine". Follia pura!
Sono passati 3 anni e sono ancora lì a smenarsela col package manager e peggio ancora hanno perso la retro-compatibilità con BeOS: l'obiettivo per cui Haiku fu fatto.
Per me meglio riscriversi tutto che farsi "imporre" le scelte di GNU...
Capisco e concordo, ma più che altro quelli di Haiku avrebbero potuto dare un'occhiata all'implementazione di Windows, che di certo non ha /etc., e fare qualcosa di simile per preservare la loro identità. ;)
Grazie se le trovi mi fai un piacere!
Vedi sopra. Inoltre ti confermo che tutte le operazioni di load comportano la conversione in extended.
Sì sono solo in dubbio se aprirlo in sezione Sistemi Operativi o qui in programmazione...
Visto che state programmando, questa sarebbe la sezione giusta.
Al più in s.o. aprite thread su discorsi generici, sulle caratteristiche che dovrebbe avere un s.o. a livello più astratto.
Mai dire mai: se il progetto e buono, e visto che è basato su .NET & compagnia, potrebbe anche ritagliarsi un futuro. ;)
Io lo spero davvero: sono stufo di usare Linux! Windows non se ne parla perché bisogna pagare la licenza e Linux è gratis.... si peccato che se poi contiamo le giornate perse, le brutte figure con i clienti (uno l'abbiamo perso quando ha visto l'architettura accrocchiata che volevamo usare, un altro lo perderemo tra qualche mese!): i costi nascosti di Linux appunto...
Sì:
"The precision-control (PC) field (bits 8 and 9 of the x87 FPU control word) determines the precision (64, 53, or 24 bits) of floating-point calculations made by the x87 FPU (see Table 8-2). The default precision is double extended precision, which uses the full 64-bit significand available with the double extended-precision floating-point format of the x87 FPU data registers. This setting is best suited for most applications, because it allows applications to take full advantage of the maximum precision available with the x87 FPU data registers.
The double precision and single precision settings reduce the size of the significand to 53 bits and 24 bits, respectively. These settings are provided to support IEEE Standard 754 and to provide compatibility with the specifications of certain existing programming languages. Using these settings nullifies the advantages of the double extended-precision floating-point format's 64-bit significand length. When reduced precision is specified, the rounding of the significand value clears the unused bits on the right to zeros."
Quindi butti soltanto via una cosa che è utilissima: lavorare a precisione più elevata di quanto fanno le altre FPU.
OK, ma d'altro canto così non rischi di ottenere risultati diversi che eseguendo lo stesso codice su ARM? O su un x64 che usa solo le istruzioni SSE? D'altro canto anche la Microsoft sembra rassegnata: aspettarsi che operazioni con FP diano li stessi risultati (anche tra versioni diverse del compilatore stesso) è impossibile!
Comunque il problema è che .NET essendo nato su x86 e forse per essere il più generici possibile internamente non opera con double, ma con un tipo chiamato F che può usere double, long double o anche (che sarebbe meglio) quad (128 bit floating point) quindi dovremmo creare un tipo F nel nostro compilatore (attualmente trattiamo double come fossero long / int64) e qual punto cadrebbe un po' il senso di aver usato SSE visto che SSE non accetta in input i FP a 80 bit saremmo sempre a fare:
1. Carica da ESP il FP di 80 bit
2. Convertilo in double e rimettilo su ESP
3. Ora fai le operazioni con SSE
Uno a questo punto potrebbe dire giustamente la FPU sarà più lenta delle SSE, ma fare tutti sti "giri" si mangia il vantaggio! Usiamo la FPU per fare tutto e via...
No, l'FPU impiega sempre le stesse risorse per elaborare le operazioni a qualunque precisione venga impostata. Si parla soltanto di troncamento dei risultati, e mai di prestazioni, com'è ovvio che sia.
Ah bene mi hai tolto un dubbio...
Purtroppo anche rileggendo non mi pare ci sia nulla di sbagliato. Anche il dump esadecimale dei valori in extended e double mi sembrano corretti, perché usano un esponente diverso (più grande nel primo caso, ovviamente), da cui si spiegano 0xbfff80 (c'è un bit in più, quello che porta a 0x80 il terz'ultimo byte) per extended e 0xbff0 per il double.
I dump sembrano corretti.
Questo mi consola, peccato che l'addizione non addiziona!
Altra prova che ho fatto ieri invece di passargli direttamente [valore] ho creato una label e ho fatto [valore] beh alla fine delle operazioni in ST0 c'era [valore] non la somma tra -1.0 e [valore]. Sto letteralmente impazzendo :Prrr:
Ma hai provato a eseguire passo passo il codice generato e vedere quali valori vengono referenziati, e i risultati prodotti?
Sì abbiamo fatto partire BOCHS con il debugger e visualizzato il contenuto dei registri in tutti gli step: non c'è verso! Dopo la fadd dentro altro al registro c'è sempre la stessa bratta...
Una cosa che volevo tentare era di scrivere la costante dentro st1 e fare la somma tra st0 e st1 così anche la constante sarebbe stata in un registro, ma anche non è possibile NASM non accetta l'istruzione:
fild st1, qword [costante]
dice che gli operandi sono sbagliati!
Bene, perché si sta generando troppo spaghetti code con tutti questi if e #if.
Beh gli #if false li ho messi io, per "preservare" il codice per la FPU ora che ho avuto questa idea della classe FPU metterò quel codice lì dentro.
Gli if soprattutto con i sotto if  per i float non mi piacciono nemmeno a me, io avrei fatto uno switch sui tipi, tanto C# non è come il C dove lo switch non funziona quasi mai... fosse per me if / else if / else if /else non dovrebbero nemmeno esistere.
Vedrai quanto sarà elegante quando userete il polimorfismo per gestire i backend. :)
Sì su queste cose un linguaggio ad oggetti darà il meglio!
Esattamente, ma immagino che non abbiate nessun ottimizzatore al momento, perché il codice è abbastanza "gonfiato" da istruzioni che riproducono quello che farebbe un emulatore.
Sì al momento non è ottimizzato nulla appunto perché dobbiamo essere sicuri che l'assembler generato corrisponda 1 a 1 con X# poi quando saremo certi ottimizzeremo l'assembler  almeno in modalità release.
Ormai ci sono pochi sviluppatori, e la comunità è pure spaccata in 4, giusto per sprecare ancora di più le poche risorse.
Già davvero un peccato che la comunità Amiga abbia fatto sta fine! Il fallimento di Commodore è stata una vera catastrofe :cry: 
Quindi riscrivere tutto il C# non se ne parla. Peccato. In questo modo rimane una grossa dipendenza.
Non è che vorrebbe dire riscrivere tutto C#, ma magari alcuni plug sarebbe da togliere (in CoreRT stanno cercando di scrivere il più possibile C# in... C#) e altri magari da aggiungere. CoreRt essendo pensato per essere multi piattaforma ci permetterebbe di certo un'implementazione più pulita visto che sarebbe chiaro dove il codice nativo per la piattaforma va scritto: sarebbe lì dove dovremmo pluggare.
Forse in futuro lo faremo, chi lo sa... tanto prima bisogna che la Microsoft renda pronto per la produzione lo stesso CoreRt, quindi...
Ci sono classi comunque che andranno sempre pluggate per esempio Console scrive direttamente sulla porta della VGA i caratteri (sì non c'è nessun context switch tra user mode e kernel mode: non esiste l'user mode :D), è totalmente diversa dalla classe Console di .NET: è uguale solo l'interfaccia!
Quindi non è C# "puro"?
Non proprio leggi il blog di Joe Duffy su Midori: tanto per iniziare il compilatore faceva stack escape analisys e si permetteva di allocare oggetti nello stack cosa che nemmeno C++/CLI ha mai osato fare (finge soltanto usa l'heap come fosse uno stack)!
Poi hanno esteso la sintassi per passare oggetti come rifermento (non ho ben capito quella parte), introdussero async con una sintassi diverso dal C# normale (ma Midori l'aveva prima, C# l'ha fatta dopo ed è pure più inefficiente!)...
Io parlavo di qualcosa di diverso: unit testing. Con tanti test che vengono esercitati sul codice che avete scritto.
Niente NASM dunque, visto che rimarrebbe tutto in C#, e con la possibilità di eseguire il codice generato dal vostro IL in una sandbox e leggerne i risultati.
Beh abbiamo una unit test: 
https://github.com/CosmosOS/Cosmos/tree/master/Tests/Cosmos.Compiler.Tests.Bcl
dentro double.cs ci sono le operazioni sui double se usassimo il .NET runtime beh testeremmo quello: è ovvio che il vero C# sa convertire un ulong in double :D
Comunque venendo da Linux lavorare su questo progetto è per me qualcosa di rivoluzionario! Cose che per voi magari saranno banali a me mi fanno rizzare i peli sulle braccia: Visual Studio che prevede il codice prima che abbia finito di scriverlo, che capisce che NON compilerà senza manco compilarlo (X# non so come sia possibile ma ha iniziato ad indicarmi che stavo sbagliando! "Volevi dire <destinationIndirect>? Non può essere <sourceIndirect> perché...), le unit test e soprattutto AppVoyer!
Lenta sarebbe soltanto la parte di mappatura del codice x86/x64, per generare l'IL finale. 
Comunque, sì: non avreste codice nativo Cosmos, ma d'altra parte Cosmos è basato su IL, e quest'ultimo potrebbe provenire da qualunque fonte. :fiufiu:
Quindi sarebbe una sorta di Wine.net? Perché tutto Windows andrebbe emulato o pensi a un target Cosmos / x86?
Certo come dovremo per esempio quando faremo il browser convertire javascript in IL a runtime potremmo farlo anche per x86, ma non lo so credo non lo faremo per motivi "politici".
Capisco e concordo, ma più che altro quelli di Haiku avrebbero potuto dare un'occhiata all'implementazione di Windows, che di certo non ha /etc., e fare qualcosa di simile per preservare la loro identità. ;)
Python era ovviamente solo un esempio, tutte le applicazioni GNU hanno quelle orride directory hardcoded per Windows il porting l'hanno fatto (anche se io ricordo anni fa di aver installato un qualcosa di GNU su Windows e di essermi trovato un C:/etc pieno di bratta), loro hanno deciso di usare questo scorciatoia: hanno perso la loro strada!
E dire che c'era una soluzione migliore da cui prendere esempio i bundle di Apple e invece cosa vanno a copiare? Il package manager di Linux!
Che poi - sarò strano io - ma cosa c'è di male a scaricare un installer dal sito del produttore e installarlo? Boh mai capito...
Vedi sopra. Inoltre ti confermo che tutte le operazioni di load comportano la conversione in extended.
Visto che state programmando, questa sarebbe la sezione giusta.
Al più in s.o. aprite thread su discorsi generici, sulle caratteristiche che dovrebbe avere un s.o. a livello più astratto.
OK vedo se riesco ad aprire un bel thread nel week end conta sulla vostra partecipazione!
cdimauro
29-06-2016, 23:18
OK, ma d'altro canto così non rischi di ottenere risultati diversi che eseguendo lo stesso codice su ARM? O su un x64 che usa solo le istruzioni SSE? D'altro canto anche la Microsoft sembra rassegnata: aspettarsi che operazioni con FP diano li stessi risultati (anche tra versioni diverse del compilatore stesso) è impossibile!
Mi pare normale che ci sia un margine di tolleranza, visto che parliamo di numeri in virgola mobile.
Non ti preoccupare di questo, ma se puoi usare la precisione massima senza intaccare le prestazioni, approfittane.
Comunque il problema è che .NET essendo nato su x86 e forse per essere il più generici possibile internamente non opera con double, ma con un tipo chiamato F che può usere double, long double o anche (che sarebbe meglio) quad (128 bit floating point) quindi dovremmo creare un tipo F nel nostro compilatore (attualmente trattiamo double come fossero long / int64) e qual punto cadrebbe un po' il senso di aver usato SSE visto che SSE non accetta in input i FP a 80 bit saremmo sempre a fare:
1. Carica da ESP il FP di 80 bit
2. Convertilo in double e rimettilo su ESP
3. Ora fai le operazioni con SSE
Uno a questo punto potrebbe dire giustamente la FPU sarà più lenta delle SSE, ma fare tutti sti "giri" si mangia il vantaggio! Usiamo la FPU per fare tutto e via...
Al momento sì, ma mi aspetto che l'uso di un tipo anziché un altro derivi dalla scelta fatta dal programmatore: se uso un single, non mi aspetto che venga poi internamente generato codice binario che usi double o addirittura extended.
extended dovrebbe essere usato soltanto se il programmatore l'ha appositamente selezionato, oppure implicitamente & internamente nel caso in cui fosse disponibile soltanto l'FPU x87.
Questo mi consola, peccato che l'addizione non addiziona!
Altra prova che ho fatto ieri invece di passargli direttamente [valore] ho creato una label e ho fatto [valore] beh alla fine delle operazioni in ST0 c'era [valore] non la somma tra -1.0 e [valore]. Sto letteralmente impazzendo :Prrr:
Sì abbiamo fatto partire BOCHS con il debugger e visualizzato il contenuto dei registri in tutti gli step: non c'è verso! Dopo la fadd dentro altro al registro c'è sempre la stessa bratta...
Una cosa che volevo tentare era di scrivere la costante dentro st1 e fare la somma tra st0 e st1 così anche la constante sarebbe stata in un registro, ma anche non è possibile NASM non accetta l'istruzione:
fild st1, qword [costante]
dice che gli operandi sono sbagliati!
Onestamente non so cos'altro pensare. Ci dev'essere per forza qualche errore che al momento sfugge, perché non è che siete i primi a generare codice per x87: lo fa persino WinUAE di recente, e pure in modalità x64!
Beh gli #if false li ho messi io, per "preservare" il codice per la FPU ora che ho avuto questa idea della classe FPU metterò quel codice lì dentro.
Gli if soprattutto con i sotto if  per i float non mi piacciono nemmeno a me, io avrei fatto uno switch sui tipi, tanto C# non è come il C dove lo switch non funziona quasi mai... fosse per me if / else if / else if /else non dovrebbero nemmeno esistere.
Non mi piacciono nemmeno gli switch: sono if mascherati. :stordita: 
Non proprio leggi il blog di Joe Duffy su Midori: tanto per iniziare il compilatore faceva stack escape analisys e si permetteva di allocare oggetti nello stack cosa che nemmeno C++/CLI ha mai osato fare (finge soltanto usa l'heap come fosse uno stack)!
Poi hanno esteso la sintassi per passare oggetti come rifermento (non ho ben capito quella parte), introdussero async con una sintassi diverso dal C# normale (ma Midori l'aveva prima, C# l'ha fatta dopo ed è pure più inefficiente!)...
Capito. Mi sa che devo buttarci un occhio, ma sono quasi sempre a corto di tempo. :muro: :cry:
Beh abbiamo una unit test: 
https://github.com/CosmosOS/Cosmos/tree/master/Tests/Cosmos.Compiler.Tests.Bcl
Allora usatela anche e soprattutto per il JITter, altrimenti diventerete matti.
Comunque venendo da Linux lavorare su questo progetto è per me qualcosa di rivoluzionario! Cose che per voi magari saranno banali a me mi fanno rizzare i peli sulle braccia: Visual Studio che prevede il codice prima che abbia finito di scriverlo, che capisce che NON compilerà senza manco compilarlo (X# non so come sia possibile ma ha iniziato ad indicarmi che stavo sbagliando! "Volevi dire <destinationIndirect>? Non può essere <sourceIndirect> perché...), le unit test e soprattutto AppVoyer!
Già. Siamo ben abituati. :D Ma AppVoyer non lo conosco, anche se il nome è tutto un "programma". :asd:
Quindi sarebbe una sorta di Wine.net?
Non esattamente. Wine mappa le API di Windows su quelle Linux, mentre la mia idea è di mappare gli opcode x86/x64 in opcode IL. In questo modo non è necessario ogni volta decodificare queste istruzioni per i blocchi di codice: si prende l'IL conosciuto e si genera direttamente e immediatamente codice binario per la specifica piattaforma.
Il tutto fino a mappare l'intero codice in IL, e a questo punto si potrebbe anche andare di compilazione AoT in un colpo solo, togliendo di mezzo il runtime/JITter.
Perché tutto Windows andrebbe emulato o pensi a un target Cosmos / x86?
Beh, le chiamate alle API Windows andrebbero ovviamente rimappate in chiamate alle API Cosmos.
L'architettura al momento lasciala perdere, perché riguarda il JITter.
Certo come dovremo per esempio quando faremo il browser convertire javascript in IL a runtime potremmo farlo anche per x86, ma non lo so credo non lo faremo per motivi "politici".
Per Javascript è abbastanza diverso, visto che non ha bytecode di default, ma solo il sorgente.
OK vedo se riesco ad aprire un bel thread nel week end conta sulla vostra partecipazione!
Metti il link. ;)
Ma ancora la FADD non "FADDa"?
Deve esserci qualcosa che non quadra: un'istruzione che viene eseguita dalla CPU ma che, contemporaneamente, non viene eseguita. Questa è una rivoluzione :sofico:
Ho fatto un'ulteriore prova:
          // Save the high part of the ulong in EAX (we cannot move all of ESP as it has 64 bit size)
          XS.Set(EAX, ESP, sourceIsIndirect: true, sourceDisplacement: 4);
          XS.FPU.IntLoad(ESP, isIndirect: true, size: RegisterSize.Long64);
          XS.Test(EAX, EAX);
          XS.Jump(ConditionalTestEnum.NotSign, LabelSign_Bit_Unset);
          XS.Set(ECX, 0x5F800000);
          XS.FPU.IntLoad(ECX, isIndirect: true, size: RegisterSize.Long64);
          XS.LiteralCode(@"fadd st1");
          XS.Label(LabelSign_Bit_Unset);
          XS.FPU.FloatStoreAndPop(ESP, isIndirect: true, size: RegisterSize.Long64);
L'idea è di provare a mettere la "constante" in un altro registro della FPU e fare la somma tra quelli peccato che:
1. 'fild' non accetta un valore come sorgente, ma solo memoria ecco perché scrivo su ECX e poi da lì sulla FPU
2. 'fild' non accetta un 'destinatario' è sempre implicitamente ST0, il valore che c'era prima non viene sovrascritto, ma spostato su ST1...
...almeno questo in teoria quello che fa a me - a sto punto perché gli sono antipatico - è di ri-copiare ST0 su ST1 (corretto), ma in ST0 non viene messo il nuovo valore. 
La fadd poi funziona, ma ovviamente il valore è -2 e ci faccio le uova con -2 io volevo 18 miliardi e passa :cry:
||ElChE||88
30-06-2016, 10:23
Ma sbaglio o stai settando il valore di ecx a quella costante e poi usandolo come se fosse un indirizzo di memoria (isIndirect: true)?
fild((QWORD *)0x5F800000)
Ma sbaglio o stai settando il valore di ecx a quella costante e poi usandolo come se fosse un indirizzo di memoria (isIndirect: true)?
fild((QWORD *)0x5F800000)
Sì perché fild accetta solo "memoria" in input, avevo il dubbio che andasse messo nello stack (quindi in ESP) e poi copiato da lì, ma in quel caso ottenevo la corruzione dello stack. D'altra parte se lo metto in ECX non è "memoria" e mi pare che me lo facesse fare solo come ptr...
Purtroppo NASM è molto meno avanzato di MASM per esempio io non ho ancora capito come tradurre questa istruzione in NASM correttamente ed è lì che sbaglio di certo!
fadd ds:uint64add
io ho fatto così perché NASM non accetta un valore come operando (un bug?):
fadd qword [0x5F800000]
Qualche idea?
Grazie!
P.S. No, non è un baco di NASM, ma un'estensione di MASM:
http://www.felixcloutier.com/x86/FADD:FADDP:FIADD.html
http://www.ray.masmcode.com/tutorial/fpuchap8.htm#fadd
come la emulo con NASM?
||ElChE||88
30-06-2016, 15:04
Perché un estensione? È questo:
DC /0	FADD m64fp	Valid	Valid	Add m64fp to ST(0) and store result in ST(0).
Io sarei curioso di vedere l'assembly generato da X#.
Ma 'm64fp' non vuol dire 'm'[emory] quindi un puntatore?
Stasera posto il NASM generato a partire da X#...
Ma 'm64fp' non vuol dire 'm'[emory] quindi un puntatore?
Stasera posto il NASM generato a partire da X#...
m64fp credo indichi un indirizzo di memoria a 64 bit, sì. :confused:
Io sarei curioso di vedere l'assembly generato da X#.
Eccolo:
; Conv_R_Un
SystemVoidCosmosCompilerTestsBclSystemDoubleTestExecute.IL_0208.0B: ;Asm
			mov dword EAX, [ESP + 4]
SystemVoidCosmosCompilerTestsBclSystemDoubleTestExecute.IL_0208.0C: ;Asm
			fild qword [ESP]
SystemVoidCosmosCompilerTestsBclSystemDoubleTestExecute.IL_0208.0D: ;Asm
			test dword EAX, EAX
SystemVoidCosmosCompilerTestsBclSystemDoubleTestExecute.IL_0208.0E: ;Asm
			JNS near SystemVoidCosmosCompilerTestsBclSystemDoubleTestExecute.IL_0208.LabelSign_Bit_Unset
SystemVoidCosmosCompilerTestsBclSystemDoubleTestExecute.IL_0208.0F: ;Asm
			mov dword ECX, 0x5F800000
		SystemVoidCosmosCompilerTestsBclSystemDoubleTestExecute.IL_0208.10: ;Asm
			fild qword [ECX]
SystemVoidCosmosCompilerTestsBclSystemDoubleTestExecute.IL_0208.11: ;Asm
			fadd st1
SystemVoidCosmosCompilerTestsBclSystemDoubleTestExecute.IL_0208.LabelSign_Bit_Unset:
SystemVoidCosmosCompilerTestsBclSystemDoubleTestExecute.IL_0208.12: ;Asm
			fstp qword [ESP]
||ElChE||88
30-06-2016, 21:06
È sbagliato, sta usando 0x5F800000 come se fosse un puntatore.
Ma 'm64fp' non vuol dire 'm'[emory] quindi un puntatore?
Si, e infatti ds:uint64add è un puntatore ad una valore nel segmento dati. Ci sara un modo per definire un valore nel genere in NASM, no? Se non c'è (molto improbabile) usa lo stack e mov o push.
E' praticamente la prima versione l'istruzione fadd viene ignorata!
	
SystemVoidCosmosCompilerTestsBclSystemDoubleTestExecute.IL_0208.00: ;Asm
          mov dword EAX, [ESP + 4]
SystemVoidCosmosCompilerTestsBclSystemDoubleTestExecute.IL_0208.01: ;Asm
	  fild qword [ESP]
SystemVoidCosmosCompilerTestsBclSystemDoubleTestExecute.IL_0208.02: ;Asm
          test dword EAX, EAX
SystemVoidCosmosCompilerTestsBclSystemDoubleTestExecute.IL_0208.03: ;Asm
          JNS near SystemVoidCosmosCompilerTestsBclSystemDoubleTestExecute.IL_0208.LabelSign_Bit_Unset
	
SystemVoidCosmosCompilerTestsBclSystemDoubleTestExecute.IL_0208.04: ;Asm
         fadd dword [__ulong2double_const4]
SystemVoidCosmosCompilerTestsBclSystemDoubleTestExecute.IL_0208.LabelSign_Bit_Unset:
SystemVoidCosmosCompilerTestsBclSystemDoubleTestExecute.IL_0208.05: ;Asm
         fstp qword [ESP]
la costante è definita così:
__ulong2double_const4 db 95, 128, 0, 0, 0, 0, 0, 0
avevo provato a definirla come intero quindi così, ma non cambiava nulla era come se ci fosse 0:
__ulong2double_const4 db 0x5F800000
||ElChE||88
01-07-2016, 02:11
Sono entrambe sbagliate.
O usi
__ulong2double_const4 db 0, 0, 95, 128
o
__ulong2double_const4 dd 5F800000h
Nulla da fare l'fadd è ignorata!
C'è qualche trusco da fare per forzare sta FPU a fare le addizioni? Noi la inizializziamo semplicemente con finit bisogna fare altro?
cdimauro
01-07-2016, 22:11
Sì, basta la finit.
Puoi provare a mettere una costante con byte completamente casuali (e cambiati a ogni generazione), e vedere cosa ottieni?
A parte questo, sarebbe utile vedere il disassembly del binario generato. Quindi non quello di X#, ma proprio i byte generati e poi disassemblati con qualche tool.
AnonimoVeneziano
02-07-2016, 01:12
Dopo aver hexpumpato (?) il long double -1.0 dal C e aver ottenuto questo schifo:
sizeof(long double) 12
-1 as long double '0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x80 0xff 0xbf 0x00 0x00 '
sizeof(double) 8
-1 as double '0x00 0x00 0x00 0x00 0x00 0x00 0xf0 0xbf '
mentre BOCHS mi mostrava sta roba:
FP7 ST0(v): raw 0xbfff:8000000000000000 (-1,0000000000) (NORMAL)
I byte sono in posizioni diverse (endianess?) ma sembra il valore che vedo nel long double quindi l'FPU è a 80 Bit!
Quindi la costante è errata ed è per questo che non va una mazza?
E soprattutto è giusto avere l'FPU a 80 Bit o è meglio averla settata come 64 bit (così da evitare continue conversioni?).
Anche la Microsoft non sembra convinta:
Perché questo? "Only at excessive cost in performance can such hardware architectures be made to perform floating-point operations with less precision"
non posso settare la FPU a 64 Bit alla partenza e dimenticarmene? Sarebbe più lenta? Padda gli altri 4 byte con 0 ogni volta?
A questo solo il nostro insider Intel può rispondere mi sa :p
La FPU x86 e' nativa a 80-bit.
Cercare di obbligarla a fare computazioni con precisione a 64-bit richiederebbe extra lavoro per assicurarsi che gli arrotondamenti siano conformanti con gli stessi arrotondamenti un hardware che lavora nativamente a 64-bit farebbe.
||ElChE||88
02-07-2016, 01:32
La FPU x86 e' nativa a 80-bit.
Cercare di obbligarla a fare computazioni con precisione a 64-bit richiederebbe extra lavoro per assicurarsi che gli arrotondamenti siano conformanti con gli stessi arrotondamenti un hardware che lavora nativamente a 64-bit farebbe.
Eh? La precisione si può settare a 32/64/80 con fldcw e non richiede nessun lavoro extra.
AnonimoVeneziano
02-07-2016, 03:21
Eh? La precisione si può settare a 32/64/80 con fldcw e non richiede nessun lavoro extra.
E' vero, ho toppato :asd:
Però effettivamente quello che dice la Microsoft è un po' fuorviante!
Forse si riferivano alla cosa di dover settare la FPU a double per calcoli con double e a float per calcoli con i float tutte le volte? Beh ma basta rimappare il tipo 'F' a double (o long double) e sei a cavallo no? Tanto io sinceramente nella mia carriera ho sempre usato double mai float quindi il fatto delle continue conversioni non lo accuserei.
C'è da dire comunque che SSE da questo punto di vista è meglio visto che ho la possibilità di fare operazioni direttamente su float e double sono anche molto più semplici da usare!
Unica pecca è che già che c'erano potevano aggiungere un hexfloat (Float128 o come volete chiamarlo) che avrebbe potuto prendere il posto del Float80 / long double per alcune applicazioni ha sicuramente senso avere un precisione così alta e la mia impressione - da profano - era che non gli sarebbe costato nulla farlo!
In realtà ora che in C hanno aggiunto il tipo decimal, non uso più nemmeno double! Io faccio calcoli monetari e voglio che i risultati siano corretti!
Comunque ho deciso a provare il mio codice su un altro OS perché ho il dubbio che qualcosa non sia inizializzato in maniera corretta in Cosmos quindi ho fatto partire l'orrenda Ubuntu e ho aperto un bel Gnome-terminal full screen (tanto la GUI a che serve su Linux? A niente :muro:) e ho prodotto sta roba:
section .data
 hello:     db 'Hello world!',10    ; 'Hello world!' plus a linefeed character
 helloLen:  equ $-hello             ; Length of the 'Hello world!' string
                                    ; (I'll explain soon)
 __ulong2double_const4: dd 5F800000h
 __ulong2double_const3: db 0, 0, 95, 128
fmt:    db "%f", 10, 0 ;The printf format, "\n",'0'
section .text
 global _start
extern	printf		; the C function, to be called
 
_start:
 ; Let's put on ESP -1 (0xFFFFFFF) as Cosmos should do
 mov EAX, 0xFFFF
 push EAX
 mov EAX, 0xFFFF
 push EAX
 ; Let's try our code
 mov dword EAX, [ESP + 4]
 fild qword [ESP]
 test dword EAX, EAX
 JNS near LabelSign_Bit_Unset
 fadd qword [__ulong2double_const3]
 LabelSign_Bit_Unset:
 fstp dword [ESP]
; How to print what is in ESP now? Can I call printf()?
 push    dword fmt	; address of ctrl string
 call    printf		; Call C function
 mov eax,1            ; The system call for exit (sys_exit)
 mov ebx,0            ; Exit with return code of 0 (no error)
 int 80h
Purtroppo la printf() mi stampa 0.0! Eppure non credo di aver toppato alla fine della conversione il mio double dovrebbe essere in ESP, io "pusho" il formato visto che la C Calling convention è all'incontrario e chiamo la printf... o no?
||ElChE||88
02-07-2016, 17:39
Occhio che printf promuove i float a double, quindi devi fare spazio nello stack e salvare come qword ptr [esp].
Mi sto incasinando con tutte ste prove! L'originale dentro Cosmos ha correttamente qword... dove è uscito sto dword?
Il valore non è più 0.0, ma 281470681808895.000000 che in hex è 0x001fffe0! Non è il valore atteso... (ma almeno è diverso da -1.0) così printf() sta leggengo garbage probabilmente...
Ho dovuto cambiarlo così perché oggi ld non era più capace di linkare il .o e ho dovuto usare GCC che vuole però il main...
section .data
 __ulong2double_const4: dd 5F800000h
 __ulong2double_const3: db 0, 0, 95, 128
fmt:    db "0x%08x", 10, 0 ;The printf format, "\n",'0'
extern	printf		; the C function, to be called
section .text
 global main
 
main:
 ; Let's put on ESP -1 (0xFFFFFFF) as Cosmos should do
 mov ECX, 0xFFFF
 push ECX
 mov ECX, 0xFFFF
 push ECX
 ; Let's try our code
 mov dword EAX, [ESP + 4]
 fild qword [ESP]
 test dword EAX, EAX
 JNS near LabelSign_Bit_Unset
 fadd qword [__ulong2double_const3]
 LabelSign_Bit_Unset:
 fstp qword [ESP]
; How to print what is in ESP now? Can I call printf()?
 push    dword fmt	; address of ctrl string
 call    printf		; Call C function
 mov eax,1            ; The system call for exit (sys_exit)
 mov ebx,0            ; Exit with return code of 0 (no error)
 int 80h
||ElChE||88
02-07-2016, 20:15
Il risultato è esatto, perché il tuo valore iniziale è 0x0000FFFF0000FFFF e non 0xFFFFFFFFFFFFFFFF come dovrebbe essere. Immagino tu intendessi
mov ecx, 0xFFFFFFFF
e non
mov ecx, 0xFFFF
Sto lavorando a 32 bit quindi non dovrebbe essere possibile scrivere in un registro più di 4 Byte... è per questo che scrivevo il valore in ESP in 2 parti...
Comunque ha compilato scrivendo in ECX 0xFFFFFFFF (non avrebbe dovuto), ma il valore ottenuto non ha comunque senso: 0x5f000000 (-5235985954719662080.000000).
Non capisco...
P.S. Ah intendevi che dovevo scrivere FFFFFFFF 2 volte sullo stack! OK... almeno il risultato ha senso, ma anche su Linux NASM genera un'instruzione vuota invece di fadd:
fano@ubuntu:~/testAsm$ gcc  long2double.o -o long2double
fano@ubuntu:~/testAsm$ ./long2double 
-1.000000
Mi dichiaro sconfitto: la macchina ha vinto sull'uomo...
Sto lavorando a 32 bit quindi non dovrebbe essere possibile scrivere in un registro più di 4 Byte... è per questo che scrivevo il valore in ESP in 2 parti...
Comunque ha compilato scrivendo in ECX 0xFFFFFFFF (non avrebbe dovuto), ma il valore ottenuto non ha comunque senso: 0x5f000000 (-5235985954719662080.000000).
Non capisco...
P.S. Ah intendevi che dovevo scrivere FFFFFFFF 2 volte sullo stack! OK... almeno il risultato ha senso, ma anche su Linux NASM genera un'instruzione vuota invece di fadd:
fano@ubuntu:~/testAsm$ gcc  long2double.o -o long2double
fano@ubuntu:~/testAsm$ ./long2double 
-1.000000
Mi dichiaro sconfitto: la macchina ha vinto sull'uomo...
Io sono sicuro che alla fine si scoprirà che c'è qualche stupida cazzatina che causa questi errori.
cdimauro
03-07-2016, 10:06
Concordo. L'FPU x87 la si usa da 30 anni e passa ormai: ci sarà qualche errore che sfugge al momento.
||ElChE||88
03-07-2016, 13:28
A me funziona cambiando
fadd qword [__ulong2double_const3]
in
fadd dword [__ulong2double_const4]
(la costante è un errore mio, nella versione db i 2 byte sono invertiti)
Ma siamo sicuri della costante: 0x5F800000? Magari non dovrebbe essere di 8 byte pure lei per fare la cosa corretta?
||ElChE||88
03-07-2016, 13:54
La costante va bene così.
http://i.imgur.com/pnNDoDi.jpg
Ci siamo ragazzi Cosmos sa fare un ridicolo cast di ulong in un double!
02:09:28.551348         Msg: Text from kernel: Assertion succeeded:
02:09:28.566356         Msg: Text from kernel: (double) from ulong operator doesn't work long is FF-FF-FF-FF-FF-FF-FF-FF value (as bytes) is
00-00 F0-43-00-00-00-00
Test completed
Che non è altro che il nostro amichetto 18446744073709551615!
Quindi 'd' al posto di una 'q' che ci ha fregati?
Mi piacerebbe capire che cosa succedeva, però...
avevamo scritto:
fadd qword[__ulong2double_const4]
ma __ulong2double_const4 era in realtà un intero a 32 bit (un double word come piace chiamarlo ad Intel :Prrr:) quindi NASM faceva un cast maldestro facendolo diventare:
00 00 00 00 5F 80 00 00
ma poi solo i primi 4 byte erano in qualche modo sommati? Quindi un'addizione con 0x00000?
Boh!
Grazie a tutti dell'aiuto... ora committo - e poi come vi ha avevo promesso apro un post dedicato a Cosmos: partecipate numerosi!
Ho aperto il thread ufficiale su Cosmos se avete quindi altre domande su Cosmos o meglio se volete partecipare al progetto scrivete qui (http://www.hwupgrade.it/forum/showthread.php?t=2775961)
||ElChE||88
03-07-2016, 16:35
avevamo scritto:
fadd qword[__ulong2double_const4]
ma __ulong2double_const4 era in realtà un intero a 32 bit (un double word come piace chiamarlo ad Intel :Prrr:) quindi NASM faceva un cast maldestro facendolo diventare:
00 00 00 00 5F 80 00 00
ma poi solo i primi 4 byte erano in qualche modo sommati? Quindi un'addizione con 0x00000?
Semplice: 0x5F800000 è la rappresentazione binaria di 18446744073709551616 in formato floating-point 32 bit, mentre 0x000000005F800000 non è la rappresentazione binaria di 18446744073709551616 in formato floating-point 64 bit.
Esempio:
unsigned int val1 = 0x5F800000UL;
unsigned long long val2 = 0x000000005F800000ULL;
unsigned long long val3 = 0x43F0000000000000ULL;
printf("%f\n%f\n%f\n", *(float *)&val1, *(double *)&val2, *(double *)&val3);
18446744073709551616.000000
0.000000
18446744073709551616.000000
Esempio:
unsigned int val1 = 0x5F800000UL;
unsigned long long val2 = 0x000000005F800000ULL;
unsigned long long val3 = 0x43F0000000000000ULL;
printf("%f\n%f\n%f\n", *(float *)&val1, *(double *)&val2, *(double *)&val3);
18446744073709551616.000000
0.000000
18446744073709551616.000000
... ed ecco il nostro 0.0000 ed ecco perché la fadd non faceva "nulla" sommava con 0!
Ottimo... è sempre la solita cosa: i computer non mentono e i programmatori sbagliano una d con una q, riguardano il codice per 20 volte (!) e per loro è giusto non c'è verso: non funziona!
Forse perché non era giusto affatto?
Comunque trovo un fascino "segreto" nell'aver usato Linux per debuggare un problema nel SO che - in futuro - lo ucciderà :Prrr:
cdimauro
04-07-2016, 07:21
Hai visto che alla fine era una cosa banale? :D
Adesso mettevi sotto con la test suite. ;)
Guarda che la test suite c'è già! Bisogna aggiungere altri test certo...
Addirittura dopo aver committato abbiamo Appvoyer che automaticamente prova a compilare (dando errore se non compila / ti sei scordato dei file) e poi esegue tutti i test e si blocca al primo errore!
Strumento davvero utilissimo...
cdimauro
05-07-2016, 06:58
Sì, ma la test suite va rinforzata, con altri, tanti bei test. ;)
Assolutamente se arrivo vivo al fine settimana (sono praticamente solo in ufficio: i capi hanno deciso che era il momento buono per andarsene in ferie :p) ho intenzione di aggiungere test per anche operazioni aritmetiche e conversioni (cast in C#) sugli interi visto che quando hanno deciso di passare a X# mentre ero distratto hanno cambiato tutto e magari ci sono stati degli errori che il testRunner deve beccare!
Anche perché ho intenzione di fare quella utilityClass per evitare le ripetizioni di codice e quindi dopo che avrò testato tutto ho intenzione di romperlo di nuovo :Prrr:
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.