Edito

Le journal d'un codeur en 68k, C et C++.
Adresse : 2^2 * (2^0 + 2^2 * (2^0 + 2^2 * (2^0)))

Rien de très passionnant en somme.

img

"Si tu veux etre underground, tu fais du kernel" 17-02-2004, ©PpHd
"Je marche seul, sans témoin, sans personne" ©JJG

Samedi 19 Novembre 2016
guilib
Bientôt deux ans que je n'ai pas posté ici. Le temps passe vite...

Aujourd'hui, je bosse sur une lib kernel pour 68k, guilib. Comme son nom si recherché l'indique, il s'agit d'une lib permettant d'afficher des "contrôles", "widgets", <choisissez le terme consacré dans votre religion>.

Comme d'habitude, après un début de projet écrit en C, je suis repassé à l'assembleur pour deux les raisons habituelles :
- c'est très, très galères de déboguer du C sur TI, faute de débogueur
- le compilateur est à la rue, c'est à s'en réveiller la nuit
En assembleur, je suis à 30 ou 40% de taille en moins pour des fonctionnalités identiques.

Le source est disponible ici : https://github.com/Folcogh/guilib

Les concepts utilisés pour cette libs sont repompés sans vergogne sur mes expériences avec Qt : parentalité entre widgets, héritage de propriétés et système de layout qui permet de positionner les éléments automatiquement.

Pour le moment, un seul type de widget, le Label (oui, un simple texte). Ca permet d'avancer pas mal sur les fonctionnalités de l'objet Widget, et sur le système de layout. Petit tour d'horizon illustré :

Posté à
17:22
 par Folco -
Mercredi 26 Novembre 2014
Les directives
Pour satisfaire aux demandes de certains, je vais poster ici la suite des aventures de mon assembleur inutile.

J'ai donc codé plus de deux kilos en 8 jours. En ce moment, je fais les directives. Je viens de finir 'equ', 'macro' et 'xdef', mais aussi toutes les ifd/nd/eq/ne/lt/gt/... .

C'est là que ça devient marrant, parce que ça peut s'imbriquer ces trucs. Heureusement que la récursivité est simple, et faisable de manière assez optimale en assembleur.

Et faut penser à macro, parce qu'une macro qui contient un 'endif' tout seul ne met pas fin au bloc if en cours. Donc c'est assez rigolo à concevoir, mais assez pénible à écrire, parce que le parsing de ce genre de chose est vite galère.
Surtout quand on veut éviter les redondances de code, donc mutualiser un peu tout ça.

Posté à
20:42
 par Folco -
Samedi 08 Mars 2014
r6 - 2014-03-08
- don't create sources files handle if swapping is forbidden 
- don't duplicate anymore sources files in the list of files to unarchive on exit 
- revamped sources files list handling: use a handle instead of a linked list in the stack to allow expressions evaluation 
- support macro for symbols (labels) 
- added <macro parameters with chevrons>, should support directives as include in macros, and even in macro parameters 
- validation of a symbol modified, now store its size in the symbols table (really easier and faster to find macros) 
- evaluation of mathematical expressions written (relocs and equates not supported yet) 
- merge symbols and macros handles

Bon, ça continue à avancer, ça avance à un bon rythme (pour moi :D ) depuis quelques mois.
Ce dernier commit, outre quelques bugfixes, apporte deux changement considérables:

1. Les fichiers ouverts ne sont plus une liste chainée dans la pile, mais un handle sur le heap.
La raison est que les macros et les paramètres de macros étant considérés comme des fichiers, ils sont ajoutés à cette liste quand ils sont rencontrés dans le source. Hors l'évaluation d'une expression mathématique est une opération récursive. L'apparition de macros dans les expressions rendait alors cette récusrivité impossible. Erreur de design, pas mal de choses à changer.

J'ai profité de ce refactoring pour simplifier l'implantation de la gestion des macros dans le parseur de source : avant toute expression "macroifiable" (un label, une instruction, la taille d'une instruction, une directive ou une opérande), il suffit d'écrire MACRO_PARAM_BEGIN. A la fin, on écrit MACRO_PARAM_END. Tout est traité en interne dans les fonctions appelées, le parseur de code proprement dit n'y voit que du feu.

2. Les <arguments de macro entre chevrons> sont désormais supportés. Ca permet d'écrire plein de choses:
mymacro macro 
        move.\0 \1 
        endm

Et ensuite:
        mymacro.w <#0,d1>

Ca marchera sans problème. Mieux, les directives, dont include, sont supportées. Ce qui permet d'utiliser include dans une directive, et même dans un argument de macro. MACRO_PARAM_END sait s'arrêter de dépoper les macros en cours avant ce fichier include, ce qui fait qu'à la fin du parsing d'un fichier inclus, les macros en cours sont reprises et terminées.

Je rappelle que la gestion des macros est faite sans préprocessing, donc sans remplacement (peu de RAM oblige...), donc les substitutions se font à la volée, en procédant à l'assemblage d'un pseudo-fichier commençant au début de la macro, ou au début de son paramètre lors de son appel, et se terminant avec endm, ou à la fin du paramètre le cas échéant.

Voilà, l'évaluateur d'expression est codé, c'était pas compliqué. Reste quand même à le valider, et à y ajouter le support des constantes symboliques et des relogements.

L'étape suivante consiste à terminer le processing des méthodes d'adressage. Certaines, les plus simples, sont déjà codées, celles nécessitant l'évaluateur d'expression n'ont pas encore été implémentées, donc tout ce qui se base sur #imm, abs.w/l, x(an), x(an,xn), x(pc) et x(pc,xn)). Ca devrait maintenant être une formalité.

Après ça, il faudra bien s'attaquer aux relogements, et à la manière dont les enregistrer. Ca, ça risque de me faire braire violemment...

Ah au fait, j'ai mis mes sources sur SF pour avoir une sauvegarde externe :
- as
- pdtlib

Pdtlib, j'en ai déjà parlée, c'est une lib dédiée à l'utilisation de softs en CLI sous PedroM, avec quelques fonctions concernant le file system, pour se passer des SYM_STR qui demandent toujours des manipulations à la con en assembleur.
Posté à
20:52
 par Folco -
Mardi 28 Janvier 2014
L'optimisation du soir
Ce soir, il m'est venu une petite optimisation. Pas grand chose bien sûr, et certainement connue par les extrémistes des routines graphiques qui arrivent à faire flamber votre 68k.

Ce code, qui remonte une simple liste chainée jusqu'à retrouver le fichier (... (grand (grand (parent))))) :
\FindBaseFile: 
	move.l	FILE.Parent(a5),d0 
	beq.s	\End 
		movea.l	d0,a5 
		bra.s	\FindBaseFile 
\End:

est équivalent à celui-ci :
\FindBaseFile: 
	move.l	FILE.Parent(a5),d0 
	exg.l	d0,a5 
	bne.s	\FindBaseFile 
 
	movea.l	d0,a5

Et voilà, niveau taille c'est pareil, mais ça permet tout simplement de sortir une instruction de la boucle.
Posté à
21:04
 par Folco -
Vendredi 25 Octobre 2013
L'idée du jour
Le chef et son commis sont en train de coder...
- Chef, chef, je veux popper un registre, j'y arrive pas :(
- Ducon, move.l (sp)+,dn, ça te parle pas ? :|
- Ben si, mais je voudrais pas changer les flags en fait
- A tout hazard, un movem.l (sp)+,dn, ça ferait pas ton affaire ?
- Ah, euh...
- 'spèce de noobstubien, pffff :o

Après la pause métaphysiquement transcendentale du chef :
- Euh, chef, c'est bien vot'truc, mais ça marche pas de registre à registre, malheureusement...
- Raaah, mais.... :| move.l dn,-(sp), puis tu revois la leçon d'avant pour la suite. Ok ? Laisse-moi méditer sur mon chouchen, j'y puise mon prochain trick. Aller dégage.
- Chef oui chef !
Posté à
22:15
 par Folco -
Samedi 19 Octobre 2013
Pdtlib - Libcalls en F-Line - system.asm
Aujourd'huin on attaque une fonction unique, toute petite en taille, mais combien puissante.
- son nom, pdtlib::InstallTrampolines
- son but immédiat : créer des trampolines vers les fonctions de la libc de PedroM et vers n'importe quelle lib dynamique
- son but éloigné : permettre l'utilisation de la libc et des bibliothèques dynamiques sans relogement

Comment ça marche :
- utilisation des Ramcalls kernel::LibsBegin, kernel::LibsPtr et kernel::LibsEnd pour obtenir l'adresse des fonctions ou variables à utiliser
- création d'une table de trampolines, quelque part en RAM, dans un handle, dans la pile ou même en section de code, afin de jumper vers les fonctions désirées

Posté à
19:07
 par Folco -
Jeudi 17 Octobre 2013
Pdtlib - Gestion des fichiers - files.asm
Je vais présenter en plusieurs étapes une lib que j'écris dans le cadre de mes programmes qui tournent sur PedroM.

Le but de cette lib est de faciliter la vie au programmeur, en contournant quelques points rébarbatifs de la programmation en assembleur, certains étant dûs au design du TIOS.

La lib aborde 5 parties distinctes :
- gestion des fichiers
- parsing d'un texte, et spécialement d'un fichier source, quel que soit le langage
- gestion de la ligne de commande, PedroM supportant les appels de la forme int main (int argc, const char** argv)
- gestion des fichiers de configuration
- utilisation des ramcalls en f-line, donc sans relogements, pour des libs dynamiques et la libc de PedroM

Cette fois-ci, j'abord la gestion des fichiers
Posté à
20:06
 par Folco -
Jeudi 18 Avril 2013
pdtlib::ManageCmdline
Et hop, encore une fonction écrite. Cette fonction est faire pour parser la ligne de commande en une instruction dans le programme, appelant la callback fournie pour chaque switch valide trouvé sur la ligne. Il y a bien sûr une callback dédiée aux arguments qui ne sont pas des switches. La valeur de retour de chaque callback permet à la fonction de savoir la suite qu'elle doit donner au traitement de la ligne de commande.

Bref, ça donne un code propre et simplissime. Quelques fonctions d'accès (GetCurrentArg, GetNextArg, ResetCmdlineParsing) permettent de rester souple, voire de procéder au parsing à la main si besoin était, ou de traiter facilement les switches demandant des arguments, sans faire paniquer le parseur. :)

Les switches peuvent être de la forme +x, -x, ++xyz ou --xyz. Ca donne de la liberté.

Voici le code, assez spaghetti mais optimisé en taille. Le côté spaghetti vient du fait que tous les branchements sont courts, mais la fonction dépassant les 128 octets, il a fallu déplacer certains morceaux de code à la fin, faisant perdre un peu en lisibilité. Mais au moins, question exécution, c'est optimal.

Du reste, si vous voyez des optimisations, je suis preneur. J'ai quand même un peu réfléchi le code, mais ça reste un premier jet :

ManageCmdline

;=============================================================================== 
; 
;       pdtlib::ManageCmdline 
; 
;       in      4(sp)   CMDLINE* cl 
;               8(sp)   void*    Data 
;               12(sp)  char*    SwitchesList 
;               16(sp)  ushort   (*ArgNotSwitch) (void* Data asm (a0)) 
;               20(sp)  ushort   (*ArgSwitch1)   (void* Data asm (a0), 
;                                                 short Sign asm (d1.w) 
;                                                ) 
;               24(sp)  (*)      ... 
; 
;       out     d0.w    ushort 
; 
;       destroy d0.w 
; 
;       Return a pointer to the current arg while parsing with ManageCmdline, 
;       or NULL if parsing is finished 
; 
;       SwitchesList format: 
; 
;       dc.b 'ShortSwitch1', "Long Switch 1", 0 
;       dc.b 'ShortSwitch2', "Long Switch 2", 0 
;       dc.b ... 
;       dc.b 0, 0 
; 
;       'ShortSwitchX' or "Long Switch X" may be 0, if no such switch exists 
;       When both are 0, this is the end of the table 
; 
;=============================================================================== 
 
pdtlib##antispam##0001: 
        movem.l a2-a5,-(sp) 
        movem.l 5*4(sp),a2-a5 
 
\Loop:  movea.l a2,a0 
        bsr.s   GetNextArg 
        move.l  a0,d0 
        beq.s   \EndOfParsing 
 
        movea.l a4,a1 
        moveq.l #0,d2 
 
        move.w  #'+',d1                 ; Clear upper byte for (*ArgSwitch) 
        cmp.b   (a0)+,d1 
        beq.s   \CheckSecondSign 
 
        addq.w  #2,d1                   ; - 
        cmp.b   -1(a0),d1 
        bne.s   \ArgNotSwitch 
 
\CheckSecondSign: 
        cmp.b   (a0)+,d1 
        beq.s   \LongSwitch 
 
        ; This is a short switch 
 
        moveq.l #PDTLIB_INVALID_SWITCH,d0 
        tst.b   (a0) 
        bne.s   \EndOfParsing 
        tst.b   -(a0) 
        beq.s   \EndOfParsing 
        moveq.l #PDTLIB_SWITCH_NOT_FOUND,d0 
 
\ShortSwitchLoop: 
        tst.b   (a1) 
        bne.s   \TestShortSwitch 
                tst.b   1(a1) 
                beq.s   \EndOfParsing 
\TestShortSwitch: 
        cmpm.b  (a0)+,(a1)+ 
        beq.s   \ShortSwitchFound 
\SkipShortSwitchLoop: 
                tst.b   (a1)+ 
                bne.s   \SkipShortSwitchLoop 
                        addq.w  #4,d2 
                        subq.l  #1,a0 
                        bra.s   \ShortSwitchLoop 
 
        ; This is a long switch 
 
\LongSwitch: 
        moveq.l #PDTLIB_INVALID_SWITCH,d0 
        tst.b   (a0) 
        beq.s   \EndOfParsing 
        moveq.l #PDTLIB_SWITCH_NOT_FOUND,d0 
        pea     (a0) 
 
\LongSwitchLoop: 
        movea.l (sp),a0 
        addq.l  #1,a1 
\TestLongSwitch: 
        cmpm.b  (a0)+,(a1)+ 
        beq.s   \TestEndOfLongSwitch 
                subq.l  #1,a1 
\SkipLongSwitchLoop: 
                tst.b   (a1)+ 
                bne.s   \SkipLongSwitchLoop 
                addq.w  #4,d2 
                tst.b   (a1) 
                bne.s   \LongSwitchLoop 
                tst.b   1(a1) 
                bne.s   \LongSwitchLoop 
                bra.s   \EndOfParsing 
 
        ; This is not a switch 
 
\ArgNotSwitch: 
        movea.l a3,a0 
        jsr     (a5) 
 
        ; Check return value of callbacks 
 
\CheckReturnValue: 
        subq.w  #1,d0 
        beq.s   \Loop 
        subq.w  #1,d0 
        bne.s   \WrongReturnValue 
        moveq.l #PDTLIB_STOP_PARSING,d0 
        bra.s   \EndOfParsing 
 
\WrongReturnValue: 
        moveq.l #PDTLIB_WRONG_RETURN_VALUE,d0 
 
\EndOfParsing: 
        movem.l (sp)+,a2-a5 
        rts 
 
\TestEndOfLongSwitch: 
        tst.b   -1(a0) 
        bne.s   \TestLongSwitch 
 
        ; LongSwitchFound 
 
        addq.l  #4,sp 
\ShortSwitchFound: 
        movea.l a3,a0 
        movea.l 9*4(sp,d2.w),a1 
        jsr     (a1) 
        bra.s   \CheckReturnValue


Fun fact : hier soir, j'ai fini de coder la vérification des valeurs de retour, le lancement des callbacks, et surtout le traitement des switches courts. J'ai relu ma fonction sans y trouver de bug apparent. Mais la nuit, j'ai fait un rêve, dans lequel je traçais ma fonction et j'y trouvais un bug. Le matin, ça m'est venu en tête, et ça m'a turlupiné toute la journée, je me suis retourné tout le code dans la tête sans parvenir à trouver ce qui clochait. Et sur le chemin du retour du boulot, je me suis souvenu de ce que j'avais rêvé, et le bug était bien là. Je crois qu'il faut que j'arrête, certains pourraient me prendre pour un fou :D
Posté à
21:49
 par Folco -
Lundi 15 Avril 2013
CreateSymStr
Et voilà, j'ai compté les cycles, les neg/movea / exg/subq sont plus rapides que les lea, qui se font plomber par le temps de calcul des adresses effectives. C'est ça qui est marrant en assembleur, une petite optimisation permet en plus de donner à son code une tournure infiniment plus l33t, qui fait toute la différence entre le vieux codard routard et le p'tit jeune qui débute.
;=============================================================================== 
; 
;       CreateSymStr 
; 
;       in      a0      char* FileName as a C-string 
; 
;       out     a0      SYM_STR of FileName 
; 
;       destroy d0/a1 
; 
;       Return the SYM_STR of FileName. FileName may contain a path. 
;       Return NULL if FileName is too long 
; 
;       Warning: stack pointer is decreased with 20 after the call, 
;       even if it fails 
; 
;=============================================================================== 
 
CreateSymStr: 
        move.l  (sp),d0 
        lea     -20(sp),sp 
        movea.l sp,a1 
        move.l  d0,(a1)+ 
        clr.b   (a1)+ 
        moveq.l #17,d0 
\Loop:  move.b  (a0)+,(a1)+ 
        beq.s   \End 
        dbf.w   d0,\Loop 
                neg.w   d0 
                move.w  d0,a1 
\End:   exg.l   a0,a1 
        subq.l  #1,a0 
        rts
Posté à
15:36
 par Folco -
Dimanche 14 Avril 2013
Et le code de AddFile
Histoire de poster quelque chose, parce que je viens de réécrire le code et que ça fait un bail que j'ai rien écrit ici :
;=============================================================================== 
; 
;       pdtlib::CreateFile 
; 
;       in      a0      char*  FileName 
;               d1.w    HANDLE hd 
; 
;       out     d0.w    0 is something fails, else != 0 
; 
;       destroy d0.l 
; 
;       Create a file whose name is FileName, and the associated HANDLE is hd 
; 
;=============================================================================== 
 
pdtlib##antispam##0005: 
        moveq.l #0,d0                   ; Return code 
        movem.l d0-d2/a0-a2,-(sp) 
        movea.l sp,a2 
 
        lea     -60(sp),sp 
        pea     (sp) 
        ROMT    ER_catch 
        tst.w   d0 
        bne.s   \End                    ; Exception caught 
 
        movea.l 12(a2),a0 
        bsr.s   CreateSymStr 
        move.l  a0,-(sp) 
        beq.s   \End2                   ; Invalid SYM_STR 
 
        ROMT    SymAdd 
        move.l  d0,(sp) 
        beq.s   \End2                   ; HS_NULL 
 
        ROMT    DerefSym 
        move.w  6(a2),12(a0) 
        not.l   (a2)                    ; Success 
 
\End2:  ROMT    ER_success 
\End:   movea.l a2,sp 
        movem.l (sp)+,d0-d2/a0-a2 
        rts

Voilà, c'est pas testé, mais ça devrair marcher.

Et pour la route, le code de CreateSymStr qui est utilisé ici :
;=============================================================================== 
; 
;       CreateSymStr 
; 
;       in      a0      char* FileName as a C-string 
; 
;       out     a0      SYM_STR of FileName 
; 
;       destroy d0/a1 
; 
;       Return the SYM_STR of FileName. FileName may contain a path. 
;       Return NULL if FileName is too long 
; 
;       Warning: stack pointer is decreased with 20 after the call, 
;       even if it fails 
; 
;=============================================================================== 
 
CreateSymStr: 
        move.l  (sp),d0 
        lea     -20(sp),sp 
        movea.l sp,a1 
        move.l  d0,(a1)+ 
        clr.b   (a1)+ 
        moveq.l #17,d0 
\Loop:  move.b  (a0)+,(a1)+ 
        beq.s   \End 
        dbf.w   d0,\Loop 
                lea     1,a1    ; neg d0/movea ? 
\End:   lea     -1(a1),a0       ; exg/subq ? 
        rts

Il reste deux petites optimisations éventuelles en vitesse, mais j'ai pas encore vérifié, en tout cas pour la taille ça change rien. Les programmeurs en C devraient crier au scandale. :)
Posté à
15:12
 par Folco -
Jeudi 15 Septembre 2011
pdtlib::AddFile
Je parlais hier d'une fonction de ma librarie, AddFile :
short Pdtlib::AddFile (const char* Filename, short Handle)

Plutôt que de montrer son implémentation, j'ai retrouvé la gymnastique que devait faire eexec pour faire une opération aussi simple qu'ajouter un fichier au file system :

AddFile

CreateSymStr: 
	lea.l	SYM_STR_FRAME+1(%fp),%a2		|buffer 
	moveq.l	#8+1+8+1-1,%d0				|counter: "folder\filename\0" 
CopySymStr2: 
	move.b	(%a0)+,(%a2)+				|copy filename 
	bne.s	Continue				|end reached 
		subq.l	#1,%a2				|SYM_STR* 
		rts 
Continue: 
	dbf.w	%d0,CopySymStr2				|else loop 
		moveq.l	#ERROR_NAME,%d7 
 
 
		.include	"errors.s" 
		.include	"byte.s"		|included here, because it allow a short branch to jump to it 
 
EndOfCopy2: 
 
	|=============================================================== 
	|	Here, create a try/endrty structure using ER_catch, 
	|	because SymAdd can fail without coming back to the program 
	|=============================================================== 
	pea.l	ERROR_FRAME(%fp)			|*frame 
	RC	ER_catch				|set the frame handler 
	moveq.l	#ERROR_ADD_SYM,%d7			|default: SymAdd has failed 
	tst.w	%d0					|test it 
	bne	ThrowError				|failed... 
	pea.l	(%a2)					|else we can add it 
	RC	SymAdd					|create symbol 
	move.l	%d0,(%sp)				|push and test its HSYM 
	beq	ThrowError				|shit... 
		RC	DerefSym			|get SYM_ENTRY * 
		move.w	%d4,12(%a0)			|and store handle 
		RC	ER_success			|pop error frame 
		bset.b	#FLAG_SYM_ADDED,%d6		|set success flag

Et voilà le merdier. C'est quand même plus sympa quand ça tient en deux lignes. :)



ps -> La CSS par défaut est nulle à chier et dépend des fins de ligne...
Posté à
20:08
 par Folco -

 RSS  - ©yNBlogs 2004

Login : - Mot de passe :