Obligement - L'Amiga au maximum

Dimanche 24 février 2019 - 01:51  

Translate

En De Nl Nl
Es Pt It Nl


Rubriques

 · Accueil
 · A Propos
 · Articles
 · Galeries
 · Glossaire
 · Liens
 · Liste jeux Amiga
 · Quizz
 · Téléchargements
 · Trucs et astuces


Articles

 · Actualité (récente)
 · Actualité (archive)
 · Comparatifs
 · Dossiers
 · Entrevues
 · Matériel (tests)
 · Matériel (bidouilles)
 · Points de vue
 · En pratique
 · Programmation
 · Reportages
 · Tests de jeux
 · Tests de logiciels
 · Tests de compilations
 · Articles divers

 · Articles in english
 · Articles en d'autres langues


Twitter

Suivez-nous sur Twitter




Liens

 · Sites de téléchargements
 · Associations
 · Pages Personnelles
 · Matériel
 · Réparateurs
 · Revendeurs
 · Presse et médias
 · Programmation
 · Logiciels
 · Jeux
 · Scène démo
 · Divers


Jeux Amiga

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Trucs et astuces

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Glossaire

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Partenaires

Annuaire Amiga

Amedia Computer

Relec

Hit Parade


Contact

David Brunet

Courriel

 


Programmation : Assembleur - NewPic (remplacement de l'image de démarrage de l'Amiga)
(Article écrit par Max et extrait d'Amiga News Tech - octobre 1991)


Histoire de changer un peu, nous allons vous présenter un programme qui présente la particularité d'être parfaitement inutile. Quoique, en y regardant de plus près...

Judicieusement baptisé NewPic, ce programme se propose de remplacer la morne et triste main de l'Amiga - vous savez, celle qui demande une disquette Workbench - par une image IFF de votre choix. Si le résultat est purement esthétique, la mise en oeuvre demande quelques connaissances des processus de réinitialisation et de démarrage de l'ordinateur, un tel programme devant être capable de résister à la réinitialisation pour resurgir inlassablement à chaque redémarrage.

Séquence frisson

Or donc, lorsque vous appuyez presque machinalement sur le bouton on/off du boîtier d'alimentation, tout un tas de choses auxquelles on n'auraient même pas pensé se passent. C'est ce que l'on appelle le démarrage à froid : d'abord, l'électronique chauffe un peu, histoire de pouvoir travailler dans de parfaites conditions. Ensuite, le logiciel prend la main et commence un véritable travail de titan, qui consiste à s'informer de la configuration matérielle et à initialiser toutes les couches logicielles du système d'exploitation.

D'un autre côté, une autre séquence de démarrage, semblable à la première pour l'essentiel mais pas identique, survient lors des démarrages à chaud, c'est-à-dire essentiellement lors d'une réinitialisation, causé soit par l'appui simultané sur les maintenant célèbres trois touches Control-Amiga gauche-Amiga droite, soit par la volonté d'un programme quelconque, soit enfin suite à un plantage de la machine. Ce qui se passe à ce moment-là est fort intéressant, et je vous propose de le découvrir avec moi.

Séquence démarrage

Il faut savoir que lorsque le microprocesseur 68000 se trouve en état de réinitialisation, un système de recouvrement mémoire fait apparaître une image de la ROM en bas de mémoire. Ceci est nécessaire parce que justement, le 68000 va chercher l'adresse de la première instruction à exécuter à l'adresse $00000004 (heu... 4 en décimal) et la valeur initiale du pointeur de pile à l'adresse $00000000. Or, si de la mémoire occupe cet espace, aucune valeur valide ne pourra y être trouvée, d'où le recouvrement évoqué plus haut. Le 68000 va chercher les informations en ce qu'il croit être l'adresse 0, alors qu'il va sans même s'en rendre compte les trouver en $FC0000.

Bref, le 68000 se retrouve maintenant en $FC00D2, où après quelques tests internes (intégrité de la ROM, version du système), il supprime le recouvrement mémoire (pour les curieux, c'est le bit 0 du port A du CIA A qui le contrôle). La mémoire est maintenant à nouveau disponible en $00000000 et jusqu'à $0007FFFF (c'est la mémoire Chip ; aucune extension mémoire n'est encore reconnue) et la table des auto-vecteurs du 68000 peut être mise en place. Ensuite, on construit ExecBase, on place son adresse en $00000004, et on appelle la routine de démarrage à chaud (je simplifie un peu, mais c'est ça).

Le démarrage à chaud consiste dans un premier temps à vérifier l'intégrité de la structure ExecBase. Si ce test échoue, un démarrage "à froid" est provoqué. Un peu plus tard, les premiers programmes "résidents" (entendez par là, résistant à la réinitialisation) sont appelés, via le vecteur ColdCapture. Plus tard encore, une fois que la pile aura été initialisée, c'est CoolCapture qui sera appelé. Après ça, Exec recherche en mémoire toutes les structures résidentes et leur passe le contrôle du processeur, le temps de leur initialisation. C'est là qu'il nous faudra chercher le moyen de devenir parfaitement résident, sans embêter les détecteurs de virus qui recherchent ces affreuses bébêtes dans ColdCapture et CoolCapture, comme VirusX par exemple. Malheureusement, d'autres antivirus recherchent également les structures résidentes et les éliminent impitoyablement de la mémoire si elles ont le malheur de ne pas pointer sur une adresse en ROM. Contre ceux-là, impossible de se protéger.

Séquence structure

Comme vous devez commencer à en avoir l'habitude, un module résident est défini par une structure que l'on peut trouver dans "exec/resident.i" :

assembleur

avec :

assembleur

Le premier mot est donc la constante $4AFC qui permet à Exec de reconnaître une structure résidente lorsqu'il parcourt la mémoire. Le second élément, RT_MATCHTAG, pointe sur la structure elle-même (donc, sur RT_MATCHWORD) et sert de deuxième vérification quant à la validité de cette structure. Parmi les champs réellement importants, on trouve également RT_ENDSKIP, qui indique la fin de la structure. Ce pointeur permet de la rallonger à volonté, afin par exemple de maintenir résidentes d'autres données. En général, le programme résident sera lui aussi logé entre la structure et l'adresse pointée par RT_ENDSKIP. L'adresse de ce programme est placée dans le champ RT_INIT.

La priorité de la structure indique dans quel ordre elle sera initialisée. Plus la priorité est élevée, plus l'initialisation arrivera tôt. Par exemple, exec.library présente une priorité de 120, celle du trackdisk.device est de 14, celle du strap (le module qui affiche la main Workbench et charge le bloc d'amorce de la disquette en DF0:), de -60 et celle de la dos.library, de 0. En général, on utilisera une priorité négative, afin d'être sûr que le système complet est initialisé. J'ai arbitrairement choisi -59, au cas où d'autres programmes résidents voudraient s'installer avant la lecture du bloc d'amorce, mais -1 aurait amplement suffit. Attention toutefois à ne pas descendre en dessous de -59, étant donné que le strap ne rend pas la main ; c'est toujours le dernier module appelé.

Seuls deux drapeaux sont possibles : d'abord, RTF_COLDSTART que l'initialisation de la structure devra être effectuée devinez quand ? Lors du démarrage à froid, oui. Ensuite, RTF_AUTOINIT indique que RT_INIT pointe non pas sur une routine personnelle, mais sur une table de données qu'Exec initialisera en appelant la fonction InitStruct(). Cela est plus particulièrement utile dans le cas de bibliothèques et de périphériques logiques et ne nous intéresse pas aujourd'hui. Nous nous contenterons donc de positionner RTF_COLDSTART.

Une dernière remarque, mais de taille, concernant les structures résidentes : elles doivent absolument se trouver en mémoire Chip pour avoir une chance d'être reconnues. Un bogue, ou plutôt une omission dans Exec limite en effet la recherche à la zone de données $00000000 à $0007FFFF.

Séquence résidente

Pour une fois, Exec ne présente aucune fonction destinée à rendre résidente une structure. Il faut tout faire par soi-même. Rassurez-vous, cela n'est pas très compliqué.

On utilise pour cela les champs KickMemPtr, KickMemTag et KickCheckSum de la structure ExecBase. KickMemPtr pointe sur une liste de structures MemList (NDLR : voire l'excellent article de Philippe Vautrin à ce sujet) dont on comprend vite l'utilité : dès que son gestionnaire de mémoire est prêt, c'est-à-dire bien avant la recherche et l'initialisation des structures résidentes, Exec alloue, via AllocAbs(), toutes les zones désignées par ces MemLists, cela pour éviter qu'elles ne soient allouées par d'autres, au risque d'écraser les données qui s'y trouvent. Cela n'est bien entendu utile que pour les modules résidents en mémoire. Donc, pour se protéger contre toute atteinte à notre vie privée, nous devrons initialiser correctement une MemList et l'insérer dans ce tableau.

KickTagPtr est un pointeur sur une liste des structures résidentes trouvées lors du démarrage à froid. Ensuite, lors d'un démarrage à chaud, Exec recherchera les structures résidentes via cette liste, et uniquement ainsi. Donc, pour être sûr qu'à la prochaine réinitialisation notre structure soit toujours présente, il faudra nous y insérer nous-mêmes. Nous verrons un tout petit plus loin le format un peu particulier de cette liste.

KickCheckSum est devinez quoi ? Une somme de contrôle, oui, qu'Exec utilise pour tester l'intégrité des données pointées par KickMemPtr et KickTagPtr. Si cette somme de contrôle est incorrecte, un démarrage à froid est provoqué, et adieu le petit programme résident. Or, en nous insérant dans ces deux listes, nous faussons la somme de contrôle. Il faudra donc la recalculer, à l'aide de la fonction SumKickData().

Enfin, un quatrième champ d'ExecBase, falcutatif celui-là et nommé ResModules, peut être utilisé. Il pointe également sur une liste des structures résidentes en mémoire, aussi bien ROM que mémoire, et est utilisé par la fonction FindResident() pour rechercher un module par son nom. Si on ne s'insère pas dans cette liste, FindResident() sera incapable de trouver notre module, mais celui-ci sera tout de même bel et bien là, et actif de surcroît.

Les formats des listes pointées par KickTagPtr et ResModules sont absolument identiques une entrée nulle indique la fin de la liste et une entrée avec le bit 31 positionné indique qu'il s'agit d'un pointeur sur une seconde liste (il faut donc effacer ce bit 31 pour obtenir l'adresse de la nouvelle liste). Toute autre valeur est considérée comme l'adresse d'une structure résidente.

Comment faire son trou dans toutes ces listes ? Deux méthodes sont possibles : soit on recherche la première entrée vide, qui représente donc la fin de la liste, et on y insère un pointeur sur une liste créée par nous-mêmes et qui ne contient que notre structure ; soit on s'insère directement au premier rang en faisant pointer KickTagPtr sur nous-mêmes. Notre liste à nous contiendra dans ce cas deux pointeurs : le premier sur notre structure résidente, le second sur la liste que pointait originellement KickTagPtr. Le raisonnement est bien entendu strictement identique en ce qui concerne ResModules.

Séquence bloc d'amorce

Nous en arrivons maintenant au point où notre code résident est appellé par Exec. Je vous rappelle qu'à ce stade, tout le système est correctement initialisé, et qu'on peut donc appeler la graphics.library pour afficher la nouvelle image (en créant non pas un écran au sens Intuition du terme, mais un bon vieux View et tutti quanti) et le trackdisk.device pour lire le bloc d'amorce.

Une fois le bloc d'amorce lu, on vérifie qu'il s'agit bien d'une disquette amorçable et si c'est le cas, on termine gentiment notre programme, après avoir libéré toutes les ressources allouées. Le véritable strap sera alors appelé à son tour ; il trouvera une disquette amorçable dans le lecteur et n'aura pas besoin d'afficher sa petite main potelée. Le reste ne concerne plus que lui. Tiens, juste une précision pendant que j'y pense saviez-vous que cette fameuse main n'est pas une image au sens propre du terme, gravée dans la ROM, mais un dessin réalisé par une suite de Move(), Draw(), FloodFill() et autres BltTemplate() ? Étonnant, non ?

Listing 1

;
; NewPic - Remplace la main du WB par une image IFF au choix.
; © 1991, Max pour ANT
;
; Usage : 'NewPic I image'  pour installer
;         'NewPic R'        pour dé-installer
;

; *******************************************************
; * ATTENTION : Un minimum de vérifications sont faites *
; *             sur la validité de l'image IFF.         *
; *******************************************************

	opt	o+,ow-

	incdir	"include:"
	include	"exec/memory.i"
	include	"exec/ports.i"
	include	"exec/resident.i"
	include	"exec/execbase.i"
	include	"devices/trackdisk.i"
	include	"devices/bootblock.i"
	include	"graphics/gfx.i"
	include	"graphics/view.i"
	include	"graphics/gfxbase.i"
	include	"libraries/dos.i"

	include	"exec/exec_lib.i"
	include	"graphics/graphics_lib.i"
	include	"libraries/dos_lib.i"

; ************************************
CALL	MACRO
	jsr	_LVO\1(a6)
	ENDM

DEFBSTR	MACRO
.len\@	dc.b	.end\@-.str\@
.str\@	dc.b	\1,10
.end\@	even
	ENDM

; Codes d'erreur renvoyés par LoadPic
E_NOERR	EQU	0	; Pas d'erreur à signaler
E_FILE	EQU	1	; Fichier non trouvé
E_IFF	EQU	2	; Pas un fichier IFF
E_ILBM	EQU	3	; Pas un fichier ILBM
E_BAD	EQU	4	; Fichier IFF corrompu

; ************************************
	rsreset
args	rs.l	2		; Variables du programme principal
DosBase	rs.l	1
stdout	rs.l	1
picadr	rs.l	1
readbuf	rs.b	12
VARSIZE	rs.w	0

	rsreset
MyMem	rs.b	0		; Variables du programme résident
mm_port	rs.b	MP_SIZE		; MsgPort
mm_tdio	rs.b	IOTD_SIZE	; IoExtED
mm_buff	rs.b	2*TD_SECTOR	; Buffer pour le trackdisk.device
mm_chng	rs.l	1		; Nb de changements de disquette
gfxbase	rs.l	1		; GfxBase
mm_oldv	rs.l	1		; OldView
mm_view	rs.b	v_SIZEOF	; View
mm_vp	rs.b	vp_SIZEOF	; ViewPort
mm_bmap	rs.b	bm_SIZEOF	; BitMap
mm_rinf	rs.b	ri_SIZEOF	; RasInfo
mm_SIZE	rs.w	0

; ************************************
Start	movea.l	$4.w,a6
	lea	VARS(pc),a5
	movem.l	d0/a0,args(a5)

	lea	dosname(pc),a1
	moveq	#0,d0
	CALL	OpenLibrary
	move.l	d0,DosBase(a5)
	beq.s	NoDos
	movea.l	d0,a6
	CALL	Output
	move.l	d0,stdout(a5)
	movem.l	args(a5),d0/a0
	clr.b	-1(a0,d0.w)
	move.b	(a0)+,d0
	beq.s	NoArgs
	andi.b	#$df,d0		; Conversion majuscule
	cmpi.b	#'I',d0		; Installation demandée ?
	beq.s	InstallTag
	cmpi.b	#'R',d0		; Dé-installation ?
	beq	RemoveTag
NoArgs	lea	usage.txt(pc),a0
	bsr	Print
Exit	movea.l	DosBase(a5),a1
	movea.l	$4.w,a6
	CALL	CloseLibrary
NoDos	moveq	#0,d0
	rts

; ************************************
InstallTag:
	move.l	a0,-(sp)
	bsr	FindMe		; Déjà intallé ?
	beq.s	InstallIt	; non
	addq.l	#4,sp
	lea	deja_la.txt(pc),a0
	bsr	Print
	bra.s	Exit

InstallIt:
	movea.l	(sp)+,a0
.space	cmpi.b	#' ',(a0)+
	beq.s	.space
	subq.l	#1,a0
	bsr	LoadPic		; Charge l'image IFF
	beq.s	.PicOk
	lea	erreurs(pc),a0
	subq.l	#1,d0		; Le code d'erreur sert d'indice
	lsl.l	#2,d0		; dans le tableau
	movea.l	0(a0,d0.l),a0
	bsr	Print
	bra.s	Exit

.PicOk	movea.l	$4.w,a6		; Alloue la mémoire nécessaire
	move.l	#RESSIZE,d0
	moveq	#MEMF_CHIP|MEMF_PUBLIC,d1	; CHIP !!!
	CALL	AllocMem
	tst.l	d0
	bne.s	.MemOk
	lea	memoire.txt(pc),a0
	bsr	Print
	bra.s	Exit

.MemOk	movea.l	d0,a4		; a4 pointe cette mémoire
	lea	RomTag(pc),a0
	lea	(RESSIZE).w,a1	; Copie les données du programme
	exg	d0,a1		; dans la mémoire réservée
	CALL	CopyMem

	CALL	Disable		; Qu'on ne me dérange pas !

	move.l	a4,RT_MATCHTAG(a4)	; Le RomTag pointe sur lui
	lea	RESSIZE(a4),a0
	move.l	a0,RT_ENDSKIP(a4)	; Fin des données résidentes
	lea	TagName-RomTag(a4),a0
	move.l	a0,RT_NAME(a4)		; Nom pour FindResident()
	lea	ResCode-RomTag(a4),a0
	move.l	a0,RT_INIT(a4)		; Routine Init

	lea	TagPtrs-RomTag(a4),a0	; Insère notre RomTag
	move.l	a4,(a0)
	move.l	KickTagPtr(a6),4(a0)	; dans ExecBase.KickTagPtr
	beq.s	.1
	bset	#7,4(a0)	; Positionne le bit 31 bu mot long
.1	move.l	a0,KickTagPtr(a6)

	lea	Modules-RomTag(a4),a0	; Insère notre module
	move.l	a4,(a0)
	move.l	ResModules(a6),4(a0)	; dans ExecBase.ResModules
	beq.s	.2
	bset	#7,4(a0)	; Positionne le bit 31 du mot long
.2	move.l	a0,ResModules(a6)

	lea	memList-RomTag(a4),a0	; Initialise et insère
	move.l	a4,ML_ME+ME_ADDR(a0)	; notre MemList dans
	move.l	KickMemPtr(a6),LN_SUCC(a0)	; ExecBase
	move.l	a0,KickMemPtr(a6)

	CALL	SumKickData
	move.l	d0,KickCheckSum(a6)	; checksum

	CALL	Enable

	lea	ok_inst.txt(pc),a0	; Et c'est tout !
	bsr	Print
	bra	Exit

; ************************************
RemoveTag:
	bsr.s	FindMe
	bne.s	RemoveIt
	lea	pas_la.txt(pc),a0
	bsr.s	Print
	bra	Exit

RemoveIt:
	movea.l	d0,a4		; a4 pointe le RomTag

	CALL	Disable

	lea	memList-RomTag(a4),a1	; Recherche NOTRE MemList...
	lea	KickMemPtr(a6),a0
.loop	move.l	LN_SUCC(a0),d0
	cmpa.l	d0,a1
	beq.s	.found
	movea.l	d0,a0
	bra.s	.loop

.found	move.l	LN_SUCC(a1),LN_SUCC(a0)	; ...et l'enlève de ExecBase

	move.l	TagPtrs-RomTag+4(a4),KickTagPtr(a6)
	bclr	#7,KickTagPtr(a6)	; Enlève notre KickTagPtr

	move.l	Modules-RomTag+4(a4),ResModules(a6)
	bclr	#7,ResModules(a6)	; Enlève notre ResModule

	CALL	SumKickData
	move.l	d0,KickCheckSum(a6)	; checkum

	CALL	Enable

	movea.l	BODYadr-RomTag(a4),a1	; Libère la mémoire du
	move.l	BODYlen-RomTag(a4),d0	; BODY de l'image IFF
	CALL	FreeMem

	movea.l	a4,a1
	move.l	#RESSIZE,d0		; Libère la mémoire des
	CALL	FreeMem			; données résidentes

	lea	ok_rem.txt(pc),a0	; Et c'est tout !
	bsr.s	Print
	bra	Exit

; ************************************
FindMe	lea	TagName(pc),a1
	movea.l	$4.w,a6
	CALL	FindResident
	tst.l	d0
	rts

; ************************************
Print	move.l	a6,-(sp)
	moveq	#0,d3
	move.b	(a0)+,d3
	move.l	a0,d2
	move.l	stdout(a5),d1
	movea.l	DosBase(a5),a6
	CALL	Write
	movea.l	(sp)+,a6
	rts

; ************************************
	include	"LoadPic.s"	; Routine de chargement IFF

; ************************************
VARS	dcb.b	VARSIZE
dosname	dc.b	"dos.library",0
	even

usage.txt	dc.b	.1-*-1
		dc.b	"NewPic V 1.0 - © 1991, Max pour ANT",10
		dc.b	"Usage : NewPic [I=INSTALL|R=REMOVE]",10
.1		even

deja_la.txt	DEFBSTR	<"NewPic est déjà installé.">
pas_la.txt	DEFBSTR	<"NewPic n'est pas installé.">
memoire.txt	DEFBSTR	<"Pas assez de mémoire !">
ok_inst.txt	DEFBSTR	<"NewPic v1.0 installé.">
ok_rem.txt	DEFBSTR	<"NewPic v1.0 dé-installé.">

erreurs	dc.l	fichier.txt,form.txt,ilbm.txt,reading.txt
fichier.txt	DEFBSTR	<"Fichier non trouvé.">
form.txt	DEFBSTR	<"Ce n'est pas un fichier IFF.">
ilbm.txt	DEFBSTR	<"Ce n'est pas un fichier ILBM.">
reading.txt	DEFBSTR	<"Erreur en lecture du fichier IFF.">

; ************************************
	include	"Resident.s"	; Données et code résident

; ************************************
	END

Listing 2

; LoadPic.s - Routine de chargement de l'image IFF

LoadPic	movem.l	d2-d7/a2-a6,-(sp)
	movea.l	DosBase(a5),a6
	lea	readbuf(a5),a4

	moveq	#E_FILE,d7	; Ouvre le fichier
	move.l	a0,d1
	move.l	#MODE_OLDFILE,d2
	CALL	Open
	move.l	d0,d4
	beq	nofile

	moveq	#E_IFF,d7
	move.l	d4,d1
	move.l	a4,d2
	moveq	#12,d3
	CALL	Read
	cmpi.l	#'FORM',(a4)	; Vérifie le 'FORM'
	bne	noiff

	moveq	#E_ILBM,d7
	cmpi.l	#'ILBM',8(a4)	; Vérifie le 'ILBM'
	bne	noiff

iff	move.l	d4,d1
	move.l	a4,d2
	moveq	#8,d3
	CALL	Read
	subq.l	#8,d0		; Réellement 8 octets lus ?
	bne	loaded
	movem.l	(a4),d5-d6	; d5=ChunkName, d6=ChunkSize

	cmpi.l	#'BMHD',d5
	beq.s	bmhd
	cmpi.l	#'CAMG',d5
	beq.s	camg
	cmpi.l	#'CMAP',d5
	beq.s	cmap
	cmpi.l	#'BODY',d5
	beq.s	body

seek	move.l	d4,d1		; Saute les Chunks inconnus
	move.l	d6,d2		; (d6 = longueur du chunk)
	moveq	#OFFSET_CURRENT,d3
	CALL	Seek
	bra.s	iff

bmhd	bsr	LoadChunk	; Charge le chunk en mémoire
	lea	width(pc),a0
	move.w	0(a3),0(a0)	; Largeur de l'image
	move.w	2(a3),2(a0)	; Hauteur de l'image
	move.b	8(a3),4+1(a0)	; Nb plans
	move.b	10(a3),8(a0)	; Compression
	bsr	FreeChunk	; Libère le chunk
	bset	#16,d7		; Flag BMHD ok
	bra.s	iff		; Chunk suivant

camg	bsr.s	LoadChunk	; Charge le chunk en mémoire
	lea	modes(pc),a0
	move.w	2(a3),(a0)	; ViewModes
	bsr	FreeChunk	; Libère le chunk
	bset	#17,d7		; Flag CAMG ok
	bra.s	iff		; Chunk suivant

cmap	bsr.s	LoadChunk	; Charge le chunk en mémoire
	movea.l	a3,a0
	lea	palette(pc),a1	; Buffer de destination
	move.l	d6,d0
	divu	#3,d0
	subq.w	#1,d0		; d0 = nb. couleurs - 1
	cmpi.w	#31,d0
	ble.s	.loop
	moveq	#31,d0		; 32 couleurs maximum !
.loop	moveq	#0,d1
	move.b	(a0)+,d1	; Composante Rouge
	lsl.w	#4,d1
	or.b	(a0)+,d1	; Composante Verte
	lsl.w	#4,d1
	or.b	(a0)+,d1	; Composante Bleue
	lsr.w	#4,d1
	move.w	d1,(a1)+	; La couleur est dans la palette
	dbra	d0,.loop	; Couleur suivante
	bsr.s	FreeChunk	; Libère le chunk
	bset	#18,d7		; Flag CMAP ok
	bra	iff		; Chunk suivant

body	bsr.s	LoadChunk	; Charge le chunk en mémoire
	lea	BODYadr(pc),a0
	move.l	a3,(a0)+	; Adresse et taille dans la MemList
	move.l	d6,(a0)		; (qui restera résidente)
	bset	#19,d7		; Flag BODY ok
	bra	iff		; ON NE LIBERE PAS LE CHUNK !!!

loaded	moveq	#E_NOERR,d6
	swap	d7
	cmpi.w	#%1111,d7	; Tous les chunks chargés ?
	beq.s	.iffok
	moveq	#E_BAD,d6	; Non, y'a une erreur...
.iffok	exg	d7,d6

noiff	move.l	d4,d1		; Ferme le fichier
	CALL	Close

nofile	move.l	d7,d0		; d0 = code d'erreur
	movem.l	(sp)+,d2-d7/a2-a6
	rts

LoadChunk:
	movea.l	$4.w,a6
	move.l	d6,d0		; d0 = d6 = sizeof(Chunk)
	moveq	#MEMF_CHIP,d1	; En CHIP si ça doit rester résident
	CALL	AllocMem	; Allocation mémoire
	movea.l	d0,a3		; a3 = adresse du Chunk
	move.l	d4,d1
	move.l	d0,d2
	move.l	d6,d3
	movea.l	DosBase(a5),a6
	CALL	Read		; et lecture du chunk
	sub.l	d6,d0		; Z=1 si erreur
	rts

FreeChunk:
	movea.l	$4.w,a6
	movea.l	a3,a1		; a1 = a3 = adresse du chunk
	move.l	d6,d0		; d0 = d6 = taille du chunk
	CALL	FreeMem		; Libération mémoire
	movea.l	DosBase(a5),a6	; (DosBase sert encore !)
	rts

Listing 3

; Resident.s - Données et code résident (initialisés ailleurs)

RomTag	dc.w	RTC_MATCHWORD	; = $4afc
	dc.l	0,0		; MatchTag, EndSkip
	dc.b	RTF_COLDSTART	; Flags
	dc.b	1,0,-59		; Version, Type, Pri
	dc.l	0,0,0		; Name, IDString, Init
TagName	dc.b	"NewPic 1.0",0
	even

memList	dcb.b	LN_SIZE
	dc.w	2		; ML_NUMENTRIES
	dc.l	0,RESSIZE	; ML_ADR, ML_LENGTH
BODYadr	dc.l	0
BODYlen	dc.l	0

TagPtrs	dc.l	0,0
Modules	dc.l	0,0

width	ds.w	1
height	ds.w	1
depth	ds.w	1
modes	ds.w	1
compr	ds.w	1
palette	ds.w	32

tdname	TD_NAME
gfxname	dc.b	"graphics.library",0
	even

ResCode	movem.l	d0-d7/a0-a6,-(sp)
	movea.l	$4.w,a6

	move.l	#mm_SIZE,d0	; Alloue les variables dynamiques
	move.l	#MEMF_CHIP|MEMF_CLEAR,d1
	CALL	AllocMem
	tst.l	d0
	beq	NoVars		; Raté (ce serait étonnant !)
	movea.l	d0,a5		; a5 pointe les vatriables

	lea	mm_port(a5),a2	; Initialise le MsgPort
	move.b	#NT_MSGPORT,LN_TYPE(a2)
	moveq	#-1,d0
	CALL	AllocSignal
	move.b	d0,MP_SIGBIT(a2)
	bmi	NoSig
	suba.l	a1,a1
	CALL	FindTask
	move.l	d0,MP_SIGTASK(a2)

	lea	mm_tdio(a5),a1	; Initialise l'IOExtTD
	move.b	#NT_MESSAGE,IO+MP+LN_TYPE(a1)
	move.l	a2,IO+MP+MN_REPLYPORT(a1)

	lea	tdname(pc),a0	; Ouvre le trackdisk.device (DF0:)
	moveq	#0,d0
	moveq	#0,d1
	CALL	OpenDevice
	tst.b	d0
	bne.s	NoTD

	lea	mm_tdio(a5),a3
	lea	mm_buff(a5),a4

	movea.l	a3,a1
	move.w	#TD_CHANGENUM,IO_COMMAND(a1)
	CALL	DoIO
	move.l	IO_ACTUAL(a3),mm_chng(a5)

	bsr.s	ChkDsk		; Y'a un disk bootable ?
	beq.s	ResExit		; Oui -> on s'casse

	bsr	Image		; Non -> on affiche l'image

ChkChng	btst	#6,$bfe001	; (on quitte avec la souris)
	beq.s	.oui

	movea.l	a3,a1		; Disque changé ?
	move.w	#TD_CHANGENUM,IO_COMMAND(a1)
	CALL	DoIO
	move.l	IO_ACTUAL(a3),d0
	cmp.l	mm_chng(a5),d0
	beq.s	ChkChng		; Pas encore

	bsr.s	ChkDsk		; Le nouveau disque est bootable ?
	bne.s	ChkChng		; Non -> boucle

.oui	bsr	UnloadImg	; Enlève l'image

ResExit	movea.l	a3,a1		; Ferme le trackdisk.device
	CALL	CloseDevice
NoTD	moveq	#0,d0
	move.b	mm_port+MP_SIGBIT(a5),d0
	CALL	FreeSignal
NoSig	movea.l	a5,a1
	move.l	#mm_SIZE,d0
	CALL	FreeMem
NoVars	movem.l	(sp)+,d0-d7/a0-a6
	rts

ChkDsk	movea.l	a3,a1
	move.w	#TD_CHANGESTATE,IO_COMMAND(a1)
	CALL	DoIO
	tst.l	IO_ACTUAL(a3)
	bne.s	.non		; Pas de disque dans le drive !

	movea.l	a3,a1
	move.w	#TD_CHANGENUM,IO_COMMAND(a1)
	CALL	DoIO
	move.l	IO_ACTUAL(a3),mm_chng(a5)

	movea.l	a3,a1		; Démarre le moteur
	move.w	#TD_MOTOR,IO_COMMAND(a1)
	addq.l	#1,IO_LENGTH(a1)
	CALL	DoIO

	movea.l	a3,a1		; ...et lit les 2 secteurs boot
	move.w	#ETD_READ,IO_COMMAND(a1)
	move.l	#2*TD_SECTOR,IO_LENGTH(a1)
	move.l	a4,IO_DATA(a1)
	clr.l	IO_OFFSET(a1)
	move.l	mm_chng(a5),IOTD_COUNT(a1)
	CALL	DoIO
	move.w	d0,-(sp)	; (sauve le code d'erreur)

	movea.l	a3,a1		; Arrête le moteur
	move.w	#TD_MOTOR,IO_COMMAND(a1)
	clr.l	IO_LENGTH(a1)
	CALL	DoIO

	tst.w	(sp)+		; Erreur de lecture ?
	bne.s	.non

	movea.l	a4,a0
	cmpi.l	#BBNAME_DOS,(a0)	; Disquette est bootable ?
	bne.s	.non
	moveq	#0,d0
	move.w	#((TD_SECTOR*2)/4)-1,d1	; Calcule son checksum
.chksum	add.l	(a0)+,d0
	bcc.s	.1
	addq.l	#1,d0
.1	dbra	d1,.chksum
	not.l	d0		; Z = 1 si le disque est bootable
.non	rts

; ************************************
Image	movem.l	a2-a6,-(sp)	; Affichage de l'image IFF

	lea	gfxname(pc),a1
	moveq	#0,d0
	CALL	OpenLibrary
	move.l	d0,gfxbase(a5)
	beq	.failed
	movea.l	d0,a6
	move.l	gb_ActiView(a6),mm_oldv(a5)

	lea	mm_view(a5),a2	; a2 pointe le View
	lea	mm_vp(a5),a3	; a3 pointe le ViewPort
	lea	mm_bmap(a5),a4	; a4 pointe la bitmap

	movea.l	a2,a1		; Initialise le View
	CALL	InitView
	move.l	a3,v_ViewPort(a2)
	move.w	modes(pc),v_Modes(a2)

	movea.l	a3,a0		; Initialise le ViewPort
	CALL	InitVPort
	move.w	width(pc),vp_DWidth(a3)
	move.w	height(pc),vp_DHeight(a3)
	move.w	modes(pc),vp_Modes(a3)
	lea	mm_rinf(a5),a0
	move.l	a0,vp_RasInfo(a3)

	move.w	depth(pc),d1
	moveq	#1,d0
	lsl.l	d1,d0
	CALL	GetColorMap
	move.l	d0,vp_ColorMap(a3)
	beq	.failed

	movea.l	a4,a0		; Initialise la BitMap
	move.w	depth(pc),d0
	move.w	width(pc),d1
	move.w	height(pc),d2
	CALL	InitBitMap

	move.w	depth(pc),d2
	subq.w	#1,d2
	moveq	#0,d3

	move.b	compr(pc),d0	; Si l'image n'était pas compressée,
	beq.s	.norast		; pas besoin d'allouer des bitplanes !

.planes	move.w	width(pc),d0
	move.w	height(pc),d1
	CALL	AllocRaster
	move.l	d0,bm_Planes(a4,d3.l)
	beq.s	.failed
	addq.l	#4,d3
	dbra	d2,.planes
	bra.s	.initRI

.norast	movea.l	BODYadr(pc),a0
	move.w	width(pc),d0
	lsr.w	#3,d0
	mulu	height(pc),d0
.noras1	move.l	a0,bm_Planes(a4,d3.l)
	adda.l	d0,a0
	addq.l	#4,d3
	dbra	d2,.noras1

.initRI	lea	mm_rinf(a5),a0	; Initialise le RasInfo
	move.l	a4,ri_BitMap(a0)

	movea.l	a2,a0		; Construit le View
	movea.l	a3,a1
	CALL	MakeVPort
	movea.l	a2,a1
	CALL	MrgCop

	move.b	compr(pc),d0	; Décompresse au besoin l'image
	beq.s	.nopack
	bsr.s	UnpackIFF

.nopack	movea.l	a3,a0		; ...et affiche le résultat
	lea	palette(pc),a1
	moveq	#1,d0
	move.w	depth(pc),d1
	lsl.l	d1,d0
	CALL	LoadRGB4
	movea.l	a2,a1
	CALL	LoadView
	CALL	WaitTOF
.failed	movem.l	(sp)+,a2-a6
	rts

UnpackIFF:
	movem.l	d0-d7/a0-a6,-(sp)	; Décompactage IFF
	move.l	BODYadr(pc),a4
	lea	mm_bmap(a5),a3
	move.w	bm_BytesPerRow(a3),d5
	moveq	#0,d0
Lignes	lea	bm_Planes(a3),a2
	move.w	depth(pc),d1
	subq.w	#1,d1
Planes	movea.l	(a2)+,a0
	move.w	d0,d2
	mulu	d5,d2
	adda.l	d2,a0
	moveq	#0,d2
Comp	moveq	#0,d3
	move.b	(a4)+,d3
	bmi.s	Crunch
	ext.w	d3
	add.w	d3,d2
	addq.w	#1,d2
CompL	move.b	(a4)+,(a0)+
	dbra	d3,CompL
	bra.s	NextByte
Crunch	cmpi.b	#$80,d3
	beq.s	NextByte
	neg.b	d3
	ext.w	d3
	add.w	d3,d2
	addq.w	#1,d2
	move.b	(a4)+,d4
CrunchL	move.b	d4,(a0)+
	dbra	d3,CrunchL
NextByte:
	cmp.w	d5,d2
	blt.s	Comp
NextPlane:
	dbra	d1,Planes
	addq.w	#1,d0
	cmp.w	bm_Rows(a3),d0
	blt.s	Lignes
	movem.l	(sp)+,d0-d7/a0-a6
	rts

UnloadImg:
	move.l	gfxbase(a5),d0	; Libère la mémoire utilisée
	beq.s	.nogfx
	movea.l	d0,a6
	movea.l	mm_oldv(a5),a1	; est-ce vraiment utile ?
	CALL	LoadView
	CALL	WaitTOF
	move.l	mm_vp+vp_ColorMap(a5),d0
	beq.s	.1
	movea.l	d0,a0
	CALL	FreeColorMap
	move.b	compr(pc),d0
	beq.s	.norast
.1	move.w	depth(pc),d2
	subq.w	#1,d2
	lea	mm_bmap+bm_Planes(a5),a2
.planes	move.l	(a2)+,d0
	beq.s	.2
	movea.l	d0,a0
	move.w	width(pc),d0
	move.w	height(pc),d1
	CALL	FreeRaster
.2	dbra	d2,.planes
.norast	lea	mm_vp(a5),a0
	tst.l	vp_DspIns(a0)
	beq.s	.3
	CALL	FreeVPortCopLists
.3	move.l	mm_view+v_LOFCprList(a5),d0
	beq.s	.4
	movea.l	d0,a0
	CALL	FreeCprList
.4	move.l	mm_view+v_SHFCprList(a5),d0
	beq.s	.5
	movea.l	d0,a0
	CALL	FreeCprList
.5	movea.l	a6,a1
	movea.l	$4.w,a6
	CALL	CloseLibrary
.nogfx	rts

RESSIZE	EQU	*-RomTag	; Taille des données résidentes


[Retour en haut] / [Retour aux articles]