18Fermer20
Kevin KoflerLe 20/09/2010 à 18:32
Folco (./13) :
Tu parles du code que t'exécutes dans le bas de l'écran, c'est pas ça ? cheeky

C'était ma solution pour les handlers d'interruptions (niveaux de gris notamment), parce que le trap #11 est beaucoup trop lent pour se taper 2 déprotections à chaque appel de l'AI1 (à l'appel et au retour, j'avais bien essayé cette solution, mais la calculatrice ne faisait plus que ça et le jeu ne marchait pas!). J'appelais cette zone de mémoire "interrupt handler area". tongue

Mais pour les appels de libs, tout était géré proprement sans aucun code sur l'écran, cette zone était vraiment réservée pour les handlers d'interruptions! Il y avait un appel du trap #11 pour l'appel de la lib et un autre pour le retour au programme principal. (Bien sûr, si tu appelais une lib toutes les 3 instructions, ça ramait à fond, mais en général les jeux ne faisaient de toute façon pas ça!)

Je redirigeais les jsr et jmp vers une lib à travers un "stub" rattaché au client (*) en temps d'exécution qui faisait:
 pea [target]
 pea [server_receiver]
 pea [server_base]
 bra caller

Le "caller" était aussi rattaché au client (*) en temps d'exécution, il s'occupait d'appeler le trap #11 pour déprotéger le serveur (*) et faire en sorte que le trap #11 retourne vers le "receiver" rattaché au serveur (*) en temps d'exécution. Le "receiver" appelait la fonction demandée de la lib, puis tout le procédé de déprotection était répété dans l'autre sens afin de retourner au "caller" du client (*), qui lui retournait à l'endroit qui avait appelé le "stub". Ce qui compliquait le tout était qu'il fallait sauvegarder tous les registres avant d'appeler le trap #11 et les restaurer après, et que, afin de garantir une compatibilité maximale, on ne pouvait absolument rien détruire, même pas %sr.

(*) J'appelle le programme ou la lib appelant "client" et le programme ou la lib appelé "serveur". Le client est souvent un programme et le serveur est en général une lib, mais le système était parfaitement flexible.

Les limitations:
List of known unsupported PreOs features in TitaniK:
* Support for non-Titanium calculators
* Everything requiring a TSR: SHIFT+ON support, AMS error intercepting,
  automatic nostub crash protection, ...
* Crash protection
* Passing command-line parameters to the programs.
* Running _nostub programs through kernel::exec.
* Programs >56 KB.
* Programs/libraries which would require >8 KB of stubs/callers/receivers.
* Programs which require all the RAM. (The stubs take away part of it.)
* Programs which require all the stack. (The stubs need lots of it.)
* The (un)reloc(2) functions won't work if you don't reserve enough room for the
  stubs at the end of the handle (NOT counted in the file size).
* Read-only libraries. (The relocation code adds at least a receiver to all
  libraries. For read-only libraries to work, this has to be skipped for them.)
* Indirect function calls to libraries or to RAM_CALLs.
* Nonstandard or excessive (>60 bytes) stack parameters to libraries or
  RAM_CALLs.
* Archive packs. (Uncompressed files need to be copied to add the stubs,
  compressed ones need TitaniK to call the uncompressor through a receiver.)
* Custom interrupt handlers that survive over a libcall or return-from-lib.
  Anything using interrupt handlers needs to be ported to use the graphlib hack.
* Programs relying on libcalls keeping the keyboard mask intact. (Could be
  fixed by saving/restoring it in the stubs.)

En bref, pratiquement toutes les fonctionnalités des kernels précédant PreOs (genre Universal OS ou TeOS) étaient gérées (sauf l'anticrash, mais TeOS ne l'avait pas non plus), certaines fonctionnalités introduites par PreOs posaient problème (et évidemment les nouveautés de PreOs 0.70 n'y étaient pas parce que TitaniK était un fork de PreOs 0.67, mais de toute façon elles auraient posé pas mal de problèmes et n'auraient probablement pas été supportés: par exemple, les libs conditionnelles impliquent des appels indirects aux fonctions, donc mon heuristique pour distinguer une fonction d'une variable ne les permettait pas), mais les vrais problèmes de compatibilité étaient ailleurs (programmes qui détruisaient la zone d'interruptions en effaçant ou copiant l'écran, programmes qui définissaient leurs propres gestionnaires d'interruptions sans utiliser l'astuce de la zone d'interruptions, programmes qui essayaient d'utiliser 0x40000 (GhostBuster pouvait aider dans certains cas, mais pas toutes les utilisations dans des programmes kernel ne sont pris en compte) etc.).