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