Python su Nintendo DS: esperienze
Transcript
Python su Nintendo DS: esperienze
Python su Nintendo DS: esperienze Lorenzo Mancini [email protected] Nintendo DS ● Nuovo handheld N ● 2 CPU ARM 66/33Mhz ● 4MB RAM ● 3D dedicato ● Touch screen ● Microfono ● Wi-Fi Risorse per sviluppatori ● Kit di sviluppo ufficiale fornito da Nintendo; ● Parallelamente: homebrewing ● ● Materiale prodotto in buona parte tramite reverse engineering; Rapporto con emulatori. Materiale per homebrewing ● ● Hardware: ● Cartuccia Slot-1 tipo “NoPass”; ● Cartuccia Slot-2 per storage; Software: ● Compilatore GCC per ARM (devkitARM); ● Librerie per accedere all'hardware (libnds); ● Regole Makefile e tool per generazione .nds; ● Layer per vari tipi di storage (DLDI). Perché Python su DS? ● Crosscompilazione verso piattaforma ARM; ● Sistema con risorse limitate; ● Wrapping librerie esistenti per hardware; ● Sperimentare con i modi di integrare python: ● extend; ● embed; ● extend + embed. Tentativi per Python su DS ● Python su DSLinux; ● PyXthonDS; ● NDSPython: ● Port di Stackless Python (2.4 / 2.5) NDSPython ● libpython 2.5 crosscompilata ARM9; ● DLDI per storage; ● Strategia embed. ● ● Hardware non esposto; Non più sviluppato. NDSPython.nds libpython2.5 DLDI Keyboard Crosscompilazione ● ● Patch realizzata dagli sviluppatori di LilyPond; In fase di revisione per il merge nel trunk di Python. “Basically, what we do is override autoconf and Makefile settings through setting environment variables.” Crosscompilazione (2) ● Argomenti --build e --host per configure; ● Servono un compilatore C e un Python nativo. CC_FOR_BUILD=gcc \ PYTHON_FOR_BUILD=python \ ./configure --build=i586-mingw32 --host=arm-eabi (il default per --build farà la cosa giusta) Crosscompilazione su DS ● Non c'è una shell; ● Non c'è un s.o.; ● Modules/getpath.c: “python/lib” hardcoded; ● Modules/Setup: non compila alcuni moduli; ● ● Stackless/stackless.h (stack switch) (già incluso); Modifica impostazioni linker. Lettura/scrittura storage ● libfat: interfaccia per storage generico; ● DLDI: sistema a plugin per libfat; dlditool scsd.dldi test_program.nds ● Framework con test suite per nuovi plugin. Wrap di libnds ● Provate varie soluzioni: ● ● ● ● ctypes: linking dinamico, impossibile importare moduli binari a runtime; swig: comodo, analisi automatica degli header file, ma parser povero e prolissità del codice generato; pyrex: buon compromesso tra lavoro per il programmatore e risultato finale: può produrre moduli Python a partire da file (.pyx) relativamente semplici (autogenerati?); Pyrex Wrap con pyrex (libnds) #define RGB15(r,g,b) (interfaccia pyrex) ((r)|((g)<<5)|((b)<<10)) def RGB15(r,g,b): return ((r)|((g)<<5)|((b)<<10)) Wrap con pyrex (codice C del modulo generato) if (!PyArg_ParseTupleAndKeywords(__pyx_args, __pyx_kwds, "OOO", __pyx_argnames, &__pyx_v_r, &__pyx_v_g, &__pyx_v_b)) return 0; Py_INCREF(__pyx_v_r); Py_INCREF(__pyx_v_g); Py_INCREF(__pyx_v_b); /* "c:\NDSPython\libnds-src-20070503\include/nds/arm9/wrap_video.pyx":3 */ __pyx_1 = PyInt_FromLong(5); if (!__pyx_1) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 3; goto __pyx_L1;} __pyx_2 = PyNumber_Lshift(__pyx_v_g, __pyx_1); if (!__pyx_2) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 3; goto __pyx_L1;} Py_DECREF(__pyx_1); __pyx_1 = 0; __pyx_1 = PyNumber_Or(__pyx_v_r, __pyx_2); if (!__pyx_1) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 3; goto __pyx_L1;} Py_DECREF(__pyx_2); __pyx_2 = 0; __pyx_2 = PyInt_FromLong(10); if (!__pyx_2) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 3; goto __pyx_L1;} __pyx_3 = PyNumber_Lshift(__pyx_v_b, __pyx_2); if (!__pyx_3) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 3; goto __pyx_L1;} Py_DECREF(__pyx_2); __pyx_2 = 0; __pyx_2 = PyNumber_Or(__pyx_1, __pyx_3); if (!__pyx_2) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 3; goto __pyx_L1;} Py_DECREF(__pyx_1); __pyx_1 = 0; Py_DECREF(__pyx_3); __pyx_3 = 0; __pyx_r = __pyx_2; __pyx_2 = 0; goto __pyx_L0; Wrap con pyrex (2) ● Come migliorare? ● Aggiungere intelligenza al tool per generare i file .pyx; (oppure) ● scrivere i .pyx a mano, valutando volta per volta la soluzione migliore. Wrap con pyrex (3) cdef extern from "nds/arm9/video.h": void videoSetMode(int mode) void vramSetBankA(int a) # ... def wvideoSetMode(int mode): videoSetMode(mode) def wvramSetBankA(int a): vramSetBankA(a) Wrap con pyrex (4) ● Si processa il file interfaccia col compilatore pyrexc: pyrexc include/nds/arm9/wrap_video.pyx -o source/arm9/wrap_video.c ● Otteniamo wrap_video.c, un modulo Python pronto da compilare. Estensione ● Idee per rendere visibile il modulo all'interprete: ● Libreria dinamica; ● Linkarlo nella libpython; ● Linkarlo staticamente con libnds. Estensione (2) ● ● Tecnica extend + embed (doc. ufficiale “Extending embedded Python”); Vantaggi: ● ● ● Basta linkare il modulo al resto della libnds, ed aggiungere del glue code; Non c'è bisogno di toccare la libpython; Svantaggi: ● Ogni volta che si wrappano nuove funzioni, bisogna ricostruire libnds ed eseguibile. Estensione (3) ● In pratica: ● ● Aggiungere la generazione di wrap_video.c nel Makefile di libnds; Linkare libnds con l'eseguibile finale ed aggiungere del glue code di questo tipo: PyMODINIT_FUNC initwrap_video(void); int main(int argc, char **argv) { Py_Initialize(); initwrap_video(); // ... Estensione (4) Sempre nell'eseguibile, passiamo il controllo allo script python: PyRun_SimpleFile(fp, "/python/main.py"); Lo script può accedere al wrapper così: from wrap_video import * wvideoSetMode(MODE_FB0) wvramSetBankA(VRAM_A_LCD) # ... Un piccolo esempio from wrap_console import * from wrap_system import * from wrap_video import * from wrap_interrupts import * from wrap_videoGL import * wconsoleDemoInit() wvideoSetMode(MODE_FB0) wvramSetBankA(VRAM_A_LCD) print "Buon PyCon Uno a tutti!" minX = -1.5 maxX = 1.5 width = 192 height = 192 aspectRatio = 1 yScale = (maxX-minX)*(float(height)/width)*aspectRatio for y in xrange(height): for x in xrange(width): c = complex(minX+x*(maxX-minX)/width, y*yScale/height-yScale/2) z=c for iter in xrange(15): if abs(z) > 2: break z = z*z+c wvramAPutPixel(x, y, RGB15((iter*2 + 24)%31, (iter*2 + 8)%31, (iter*2 + 16)%31)) Come provare? ● Lo stato degli emulatori è molto acerbo; ● Il miglior emulatore attualmente è no$gba: ● ● va in crash su esempi C banali di libnds; ● esiste una versione con debugger ($); ● emula male il flash storage (che serve!); Usare un vero DS rallenta moltissimo il ciclo di sviluppo (scrivi su storage, accendi, prova...). Sviluppi futuri ● ● ● Completare incrementalmente il wrap di libnds; Aggiungere wrapper per libwifi; Migliorare l'environment attuale (syntax check, online debug, ...); ● Riabilitazione dei moduli esclusi; ● Distribuire un singolo file .nds; ● Migliorare le performance :) Conclusioni ● Provata crosscompilazione di Python; ● Sperimentato porting su piattaforma custom; ● Proposta infrastruttura per accedere all'hardware del DS. Contacts [email protected] http://www.develer.com/~lmancini/dspython