Questo sito utilizza cookies solo per scopi di autenticazione sul sito e nient'altro. Nessuna informazione personale viene tracciata. Leggi l'informativa sui cookies.
Buongiorno, volevo sentire il vostro parere su un paio di stranezze che mi stanno capitando.
Premetto di aver letto il post in alto che dice che i nabbi rischiano il linciaggio se dicono che il compilatore è buggato (o se vogliono fare un sistema operativo, ma questa è un'altra storia... ).
Devo compilare del sorgente C in asm, e utilizzo il seguente comando:
La cosa strana è che ottengo un comportamento differente a seconda che metta le ottimizzazioni (di qualsiasi livello) o meno, ma in teoria ciò che è afflitto, secondo me dovrebbe essere indipendente dalle ottimizzazioni.
Mi spigo meglio illustrando il caso.
All'inizio del codice c'è questo:
Codice sorgente - presumibilmente C++
#define ASM_CMT(str) asm ("@; " str)
#define MY_FUNC \
ASM_CMT("-----------------");\
ASM_CMT("-----------------");\
ASM_CMT("-----------------");\
ASM_CMT("-----------------");\
ASM_CMT("-----------------");\
ASM_CMT("-----------------");\
ASM_CMT("-----------------");\
asm (".align 8, 0x00");\
asm (".ascii \"MAIN\" " ) ; \
asm (".align 4, 0x00");\
ASM_CMT("-----------------");\
ASM_CMT("-----------------");\
ASM_CMT("-----------------");\
ASM_CMT("-----------------");\
ASM_CMT("-----------------");\
ASM_CMT("-----------------");\
ASM_CMT("-----------------")
E alla fine c'è questo:
Codice sorgente - presumibilmente C/C++
MY_FUNC;
int main ()
{
return 0;
}
ovvero, ho la necessità che prima del codice del main ci sia la scritta "main" in ASCII, preceduta e seguita da molti NOP (0x00 si assembla in una add che non fa niente) grazie alla direttiva align.
Tuttavia, con o senza ottimizzazioni ciò che ottengo non è uguale, mentre secondo me dovrebbe esserlo. Il priblema è che ciò che voglio lo ottengo senza ottimizzazioni, mentre a me farebbero comodo (eliminano fino alla metà delle istruzioni)
ecco il risultato senza ottimizzazioni (giusto):
Codice sorgente - presumibilmente Plain Text
@; -----------------
@; -----------------
@; -----------------
@; -----------------
@; -----------------
@; -----------------
@; -----------------
.align 8, 0x00
.ascii "MAIN"
.align 4, 0x00
@; -----------------
@; -----------------
@; -----------------
@; -----------------
@; -----------------
@; -----------------
@; -----------------
.thumb
.syntax unified
.align 2
.global main
.code 16
.thumb_func
.type main, %function
.main:
.fnstart
.LFB13:
push {r7, lr}
add r7, sp, #0
movs r3, #0
movs r0, r3
mov sp, r7
@ sp needed
pop {r7}
pop {r1}
bx r1
.cantunwind
.fnend
.size main, .-main
.ident "GCC: (devkitARM release 45) 5.3.0"
a parte l'essere "molto sporco" rispetto alla scrittura a mano (e quello si perdona, non è ottimizzato, e poi non è scritto a mano per natura. dopotutto si tollera un piccolo overhead in cambio del lavoro pesante - tra l'altro usa un sacco di direttive che io non avevo mai visto e senza andava sempre tutto bene ), ottengo effettivamente la scritta in ascii e le nop.
mentre con le ottimizzazioni attive, all'inizio del file trovo questo:
Codice sorgente - presumibilmente Plain Text
.syntax unified
.cpu arm7tdmi
.fpu softvfp
.eabi_attribute 20, 1
.eabi_attribute 21, 1
.eabi_attribute 23, 3
.eabi_attribute 24, 1
.eabi_attribute 25, 1
.eabi_attribute 26, 1
.eabi_attribute 30, 1
.eabi_attribute 34, 0
.eabi_attribute 18, 4
.thumb
.syntax unified
.file "test.C"
.syntax divided
@; -----------------
@; -----------------
@; -----------------
@; -----------------
@; -----------------
@; -----------------
@; -----------------
.align 8, 0x00
.ascii "MAIN"
.align 4, 0x00
@; -----------------
@; -----------------
@; -----------------
@; -----------------
@; -----------------
@; -----------------
@; -----------------
e alla fine questo:
Codice sorgente - presumibilmente Plain Text
.global main
.code 16
.thumb_func
.type main, %function
.main:
.fnstart
.LFB13:
movs r0, #0
@ sp needed
bx lr
.cantunwind
.fnend
.size main, .-main
.ident "GCC: (devkitARM release 45) 5.3.0"
Ovvero mette all'inizio del sorgente qualsiasi comando "asm" che si trovi fuori da una funzione. Ho anche provato a farlo senza macro, ma senza successo. Ciononostante il codice è notevolmente migliorato, sembra quasi scritto a mano (anche se rimangono cose che non avrei mai pensato di mettere).
Esiste una soluzione a questo? O dovrò rassegnarmi a metterlo a mano dopo ogni compilazione/fare a meno delle ottimizzazioni??
La seconda stranezza, meno importante e non riguarda direttamente il gcc, è la seguente. Come saprete, ci sono le etichette che sono delle parole seguite dai due punti, e le direttive, che sono dei punti seguiti da parole e talvolta altre cose. Se scrivo del codice asm a mano e compilo nessun problema. ma se compilo il codice venuto fuori dal gcc, l'assemblatore mi fa un loop infinito ad ogni salto.
allora sono stato costretto a sviluppare un programma java che carica in una lista ogni riga dell'output del gcc e aggiunge il punto all'inizio se la riga è un'etichetta. In questo modo ho scoperto che non ho più i cosiddetti "here: B here" dovuti a un'etichetta errata. Il problema è che questa cosa mi sa di "dirty trick" (infatti punto-parola-due punti è utilizzato per i pool di costanti), oltre al fatto che non capisco perchè succeda.
grazie, buona giornata.
Ultima modifica effettuata da Bonnox il 05/07/2016 alle 15:41
Ma quale sarebbe il risultato che vuoi ottenere?
In generale non puoi sperare che il compilatore produca l'assembly che vuoi tu, quindi se vuoi qualcosa di preciso devi fare in altro modo.
ho trovato una possibile flag "-fno-toplevel-reorder"
ora provo e faccio sapere
Testo quotato
Postato originariamente da lumo:
Ma quale sarebbe il risultato che vuoi ottenere?
In generale non puoi sperare che il compilatore produca l'assembly che vuoi tu, quindi se vuoi qualcosa di preciso devi fare in altro modo.
vorrei semplicemente che prima di una funzione ci sia dello spazio vuoto con una scritta, perchè mi serve sapere il punto di inizio del codice nel file binario compilato, senza stare a guardare manualmente con il disassemblatore.
Ultima modifica effettuata da Bonnox il 05/07/2016 alle 20:54
grazie mille, la flag che ho scovato grazie a quella pagina funziona a meraviglia!!
spiego la seconda stranezza.
di norma nello gnu assembler ci sono le direttive, tipo questa:
Codice sorgente - presumibilmente Plain Text
.align 2
che iniziano con un punto.
poi ci sono le etichette:
Codice sorgente - presumibilmente Plain Text
loop:
che terminano con i due punti.
non so perchè, ma quando compilo l'output datomi con lo switch -S, l'assemblatore sembra che non accetti le etichette, per cui qualsiasi salto in realtà salta a sè stesso. Giuro, lo so che è incredibile. Infatti quando scrivo del codice asm a mano, che è mooolto più corto, va tutto bene. inspiegabile.
comunque ho fatto un programma (in java perchè con poche righe fai il mondo) che legge il listato e sostituisce le etichette con queste cose che sono un misto tra etichetta e direttiva:
Codice sorgente - presumibilmente Plain Text
.nome:
che è usato per indicare dei luoghi da cui prendere valori (pool).
Infatti ho scoperto che se cambio le etichette in questo nuovo formato, l'assemblatore non genera più salti a sè stessi.
con salti a sè stessi intendo delle istruzioni di salto che in qualche modo cambiano di 0 il PC.
tipo
Codice sorgente - presumibilmente Plain Text
here: B here
ciao e grazie
ho fatto un doppio post, come cancello questo per unirlo al precedente?
Ultima modifica effettuata da Bonnox il 05/07/2016 alle 20:53
signori scusatemi , sono riuscito a risolvere il "grosso problema" che citerò più sotto.
Come? ricordandomi che il tizio da cui ho preso l'idea aveva scritto "static inline", mentre io pensai qualcosa come " che significa static? è C, non C++, io so che in java (e dunque presumo in C++) static significa che non dipende da un oggetto, ma qui non so cosa faccia, dunque non lo metterò, ecco." invece ora ho pensato "ma se il tizio l'ha messo, ci sarà un motivo? ok non so cosa vuol dire, ma proviamo giusto per curiosità"... e vaa!
unici 2 problemi:
- peccato che però non "inlinea" comunque la funzione
- cosa significa "static"?
per chi è curioso di sapere come andava a finire prima di "questa notizia bomba" (alla top gear), lascio sotto il post originale.
(ma si può fare uno spoiler?)
all'inizio avevo detto che uso questo comando (qui ho semplificato le parti inutili):
uso il devkitpro per sviluppare sul gameboy advance.
poi, dopo aver ottenuto il file s, lo assemblo (il comando dell'assemblatore l'ho copiato da internet):
Codice sorgente - presumibilmente C/C++
as -a -mthumb -mthumb-interwork "%src%"
objcopy --verbose -O binary a.out "%dest%"
e lo innesto in un gioco già compilato (lo so che è un comportamento demoniaco, ma mi serve).
prendendo un semplice file C tipo questo:
Codice sorgente - presumibilmente C++
int chiamata (char, short);
void prova ()
{
int a = chiamata (0xB, 10);
}
int chiamata(char a, short b)
{
return((short) a + b);
}
senza ottimizzazioni (perchè se no mi segava questo esempio con la variabile non utilizzata) si genera una cosa del genere:
Codice sorgente - presumibilmente C/C++
.syntax unified
.cpu arm7tdmi
.fpu softvfp
.eabi_attribute 20, 1
.eabi_attribute 21, 1
.eabi_attribute 23, 3
.eabi_attribute 24, 1
.eabi_attribute 25, 1
.eabi_attribute 26, 1
.eabi_attribute 30, 6
.eabi_attribute 34, 0
.eabi_attribute 18, 4
.thumb
.syntax unified
.file "provablpiero.c"
.text
.align 2
.global prova
.code 16
.thumb_func
.type prova, %function
prova:
push {r7, lr}
sub sp, sp, #8
add r7, sp, #0
movs r1, #10
movs r0, #11
bl chiamata
movs r3, r0
str r3, [r7, #4]
nop
mov sp, r7
add sp, sp, #8
@ sp needed
pop {r7}
pop {r0}
bx r0
.size prova, .-prova
.align 2
.global chiamata
.code 16
.thumb_func
.type chiamata, %function
chiamata:
push {r7, lr}
sub sp, sp, #8
add r7, sp, #0
movs r2, r0
adds r3, r7, #7
strb r2, [r3]
adds r3, r7, #4
adds r2, r1, #0
strh r2, [r3]
adds r3, r7, #7
ldrb r2, [r3]
adds r3, r7, #4
movs r1, #0
ldrsh r3, [r3, r1]
adds r3, r2, r3
movs r0, r3
mov sp, r7
add sp, sp, #8
@ sp needed
pop {r7}
pop {r1}
bx r1
.size chiamata, .-chiamata
.ident "GCC: (devkitARM release 45) 5.3.0"
tralasciando tutte le cose inutili tipo eabi attribute che manco so cosa sia e il walzer dello stack pointer, si vede chiaramente che chiama con un BL la funzione identificata con l'etichetta.
ma in realtà non è questo il mio vero problema, in quanto una passata con un semplice programma che legge l'output e lo corregge mettendo il punto all'inizio dell'etichetta basta a far funzionare il tutto.
Vorrei tuttavia approfittare dell'occasione per chiedervi un'altra cosa, ma sempre riguardante questi argomenti.
Forse sbaglio qualcosa io, ma procediamo con ordine. vi spiego tutto il setup.
voglio inserire nel gioco GBA delle mie funzioni in C, e avevo pensato di facilitare lo sviluppo mettendole tutte una di seguito all'altra, e avere una specie di main richiamato dal gioco che legge in una locazione di ram (indirizzo 0x02......) il numero della funzione da richiamare. In questo modo avrei potuto aggiornare facilmente le funzioni nel caso ci fossero stati problemi o per aggiungere funzionalità, senza stare troppo a trafficare con i files.
il trucco sta nel mettere una parola chiave tipo 0xCAFEBABE (ho usato uno stupido "start of frame" come parola chiave, in onore del TCP ), che il mio programma java avrebbe rilevato nel file binario, e grazie a questo aggiustato un altro piccolo file ad offset fisso che ha solo il compito di "linkare" (da branch and link) al finto main che seleziona la funzione da avviare.
Infine il programma java assembla nella ROM questa piccola routine insieme al file binario compilato.
Tuttavia ho alcuni problemi, di diversa entità. Quello più grave, per cui chiedo a voi una mano, è che se metto "troppe" funzioni nel sorgente C (al momento circa 16), quando assemblo il tutto e faccio il debug con l'emulatore, vedo che quando bisogna fare un branch and link il program counter "va per i campi", termine simpatico che ho inventato per dire che va ad un offset dove non c'è assolutamente alcuna funzione. In quel punto capita sempre di trovare parecchie istruzioni di LSR/LSL (logical shift), ed è molto curioso. potrebbe trattarsi di padding, ma ne dubito. E ovviamente il gioco crasha. Questo è un comportamento molto misterioso, perchè ho ricontrollato più volte il sorgente asm. Forse ho sbagliato qualche parametro dei vari compilatori/assemblatori? Non vorrei aver sbagliato qualcosa nel programmino java, anche se mi sembra strano, perchè fino a 10-12 funzioni va perfettamente! Adesso mentre sto scrivendo sono andato a controllare: ho eseguito il passaggio manualmente (prima mi affidavo ai tools di code blocks e ai batch), assemblando il file .S. e in effetti è sbagliato: non corrisponde.
Ho pensato sbagliassi qualcosa nella funzione, così ne ho creata una completamente diversa, ma ancora il problema si ripresenta (branch and link dove non v'è niente).
Ancora, ho provato a mettere il main prima di tutte le implementazioni delle funzioni, e dopo le dichiarazioni, ma senza successo.
Vi segnalo inoltre che aggiungere le funzioni non causa problema di esecuzione, ma soltanto chiamarle.
Cosa sbaglio? ve ne sarei tanto grato. (scusate la figura nabba e alcune probabili oscenità nel codice)
va beh, senza ulteriori indugi vi passo tutto il materiale.
piccola routine collocata ad un offset fisso nella ROM, verrà chiamata dal gioco e caricherà le mie funzioni:
Codice sorgente - presumibilmente Plain Text
.THUMB
.ALIGN 2
PUSH {r0-r7, lr}
ldr r1, .base
BL LINKER
POP {r0-r7, pc}
LINKER:
BX R1
.align 2, 0x00
.base:
.word 0xAABBCCDD
la word verrà cambiata dal programma java che mette insieme tutto.
programmino in java, nel caso servisse (in generale non dovrebbe servire, lo metto lo stesso però non deridetemi, è stato programmato molto "alla buona", se non rispetta certi canoni stilistici, skippate al paragrafo successivo per non subire danni agli occhi
ecco il codice sorgente. cerco di spiegare il tutto, ma avviso che è un'adattamento di un mio appunto.
quando dirò "variabile 0x800D" significa essenzialmente "indirizzo di memoria inutilizzato".
Testo quotato
il file "test.c" è quello che unisce tutto: contiene sia la libreria che le funzioni. il metodo main legge la variabile 0x800D ed esegue la routine corrispondente!
poi quando le singole routine raggiungeranno una certa complessità verranno pure loro separate nel loro file C e incluse. So che per certa gente seria è impensabile, abominevole, demoniaco, #includere i file C e ricompilare il mondo. è naive ma funziona; non so i makefiles, e poi la ROM è una struttura parecchio statica: bisogna cercare di prevedere cosa uscirà pena errori orribili! Ecco cosa mi ha portato a questa scelta.
il file header è unico, ho scelto così perchè tanto la libreria se uno la vuole se la include tutta, non è che risparmi chissà chi se non vuole qualche modulo; prima ogni pezzetto in C aveva il suo header, ben schermato con l'#ifdef, per carità, ma poi ovviamente è successo il fattaccio (prima o poi sarebbe dovuto accadere, era una questione di tempo) :p sicuramente colpa di qualche stupido punto e virgola, mi ha mandato a lucciole tutti gli include.... così poi li ho uniti! ahaha ^^
ogni pezzetto svolge precisi compiti:
- math è nato come utilità per fixed floating point (anche se non ne troverai traccia perchè devo ancora studiarci su :p ), poi ho deciso di mettere anche i div, poi ne ho tolto uno, poi li ho uniti, boh i div sono un casino.
- bios contiene principalmente asm per usare il bios del GBA. i div sono problematici, non so come farlo ritornare, eccetera. poi ne ho messi ancora pochi altri (anche sotto forma di #define se non prendono argomenti, così sono più leggeri ). ovviamente bisognerà metterli quasi tutti, a parte il suono e gli affini che non interessano a nessuno.
- util contiene... utilità. non saprei definirlo in altro modo. occhio che ha il vizio di ritornare le cose nella variabile 800D (e byte seguenti)
- video dovrebbe contenere robe tipo inizializza i modi grafici, mostra immagini, copia i tils.... ma per ora contiene solo un orrenda funzione con un casino di parametri short che quadruplicano il listato asm a forza di shift per conversioni forzate... e non va bene.
[...]
da qui in poi, se aggiungo una funzione, succede il patatrak, ovvero non la chiama e va in posti sperduti del binario.
al momento c'è il file "text", che contiene una specie di sistema di testo. è ancora tutto in fase embrionale, c'è un po' di disordine e mancano funzionalità (tipo c'è il put carattere ma non il put stringa, e nemmeno la gestione della console).
la mia idea era di ricreare la libreria tipo tonc o libgba, non essendo riuscito a farle funzionare.
tanto comunque saranno pochi altri i metodi che dovrò inserire.
grazie ancora.
non fate caso a scritte strane
file test.c:
Codice sorgente - presumibilmente C++
#include "libonnox.h"
#include "libonnox/bios.c"
#include "libonnox/math.c"
#include "libonnox/util.c"
#include "libonnox/video.c"
#include "libonnox/text.c"
/* TODO
problemi:
- se metto troppe funzioni, l'as va per campi.
- fascia nera a sx del gba
problemi risolti:
----------
problemi boh:
- gcc ottimizz sbaglia a tradurre blockfill in versione c. mentre in versione asm da qualche tempo mi fa un b here che non riesco a evitare. vedere tale file x dettagli
mettendo la flag da -O2 a -O1, il C funge :)
non e' vero, ora non va piu, devo toglierle del tutto :c