Obligement - L'Amiga au maximum

Dimanche 21 juillet 2019 - 19:18  

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

 


Dossier : Les interruptions sous Exec
(Article écrit par Loïc Far et extrait d'Amiga News Tech - octobre 1991)


L'Amiga utilise de manière peu orthodoxe les sept niveaux d'interruption du 68000. Grâce à Paula, il est capable de délivrer pas moins de 15 sources d'interruptions différentes, classées par priorité.

Comme on était en droit de s'y attendre, Exec tire pleinement parti de cette particularité du matériel, à tel point que les interruptions sont la base même de son noyau multitâche. Exec décode les demandes d'interruptions, les évalue et se branche sur les routines adéquates, en fonction d'un certain nombre de paramètres que vous allons voir ici. En plus des niveaux de priorité, Exec distingue deux types d'interruptions : les interruptions en provenance du matériel, bien sûr, mais aussi celles provoquées par logiciel. Enfin. Exec permet l'inhibition complète et la restitution de toutes les interruptions sur simple demande d'une application.

Comment ça marche

Avant qu'une routine d'interruption ne soit appelée, plusieurs étapes, tant matérielles que logicielles, sont exécutées. La séquence exacte de ce qui se passe est la suivante :

1. Un périphérique quelconque décide de causer une interruption et envoie pour ce faire un signal à Paula.

2. Paula prend en compte cette demande et la "note" en positionnant le bit correspondant du registre matériel INTREQ. Puis elle examine le bit correspondant dans INTENA pour déterminer si cette interruption est autorisée ou non. Si et seulement si c'est le cas, Paula la transmet au 68000, non sans avoir auparavant transformé le niveau de priorité en un acceptable par le microprocesseur.

3. Le 68000 décode à son tour cette demande d'interruption et détermine si elle est valide ou non. Une interruption n'est valide que lorsque son niveau de priorité est supérieur à celui dans lequel se trouve le microprocesseur au moment de la demande.

4. Si la demande d'interruption est valide, le processeur passe alors en mode superviseur (s'il ne l'était pas déjà), et sauvegarde sur la pile les informations relatives à son état actuel (registres SR et PC), puis il se place dans le même niveau de priorité que celui de l'interruption.

5. Le 68000 cherche maintenant dans sa table d'auto-vecteurs d'interruptions, l'adresse de la routine à exécuter (note sur un 68000, cette table se trouve aux adresses $64 à $7C incluses. Sur un 68020 ou 68030, un registre supplémentaire nommé VBR - pour Vector Base Register - permet de reloger cette table n'importe où dans l'espace d'adressage du processeur).

6. Le 68000 saute à l'adresse lue dans la table. Il s'y trouve une routine d'Exec qui doit maintenant décoder à nouveau cette demande d'interruption, afin d'en retrouver l'originaire. Ceci est réalisé au moyen des registres matériels INTREQ et INTENA. Une fois l'origine de l'interruption déterminée, Exec recherche dans la structure ExecBase l'adresse de la routine à exécuter pour ce type d'interruption.

7. Exec appelle finalement cette routine qui peut, dans le cadre de serveurs d'interruption, en appeler encore d'autres.

Comme on peut le voir, cela est loin d'être simple !

Au retour de chaque interruption, Exec détermine si le temps imparti à la tâche en cours est écoulé ou non, ou si une tâche a reçu un signal qu'elle attendait (via Wait() ou WaitPort()), auquel cas le gestionnaire de tâches est appelé et le temps d'occupation du processeur pour chaque tâche est recalculé.

Comme vous le voyez, Exec repose lourdement sur les interruptions pour gérer son multitâche. Si, pour une raison quelconque, les interruptions ne survenaient plus, la tâche courante utiliserait tout le temps processeur pour elle toute seule, plus rien ne venant la forcer à l'abandonner quelques instants.

Les priorités des interruptions

Les interruptions sont classées par priorité à deux niveaux : d'abord p4r le 68000 lui-même, puis par le logiciel qui, à l'intérieur de chaque niveau de priorité du processeur, introduit des pseudo-priorités. Ces pseudo-priorités déterminent l'ordre d'exécution de chaque interruption pour une priorité 68000 donnée.

Il existe en fait deux types de gestionnaires d'interruptions : les "handlers" (gestionnaires), qui en sont les propriétaires exclusifs, et les serveurs, qui se partagent l' interruption.

Le tableau ci-dessous indique pour chaque niveau d'interruption, son type de gestionnaire.

interruptions

La colonne de droite, INTREQ, indique les noms de bits, tels qu'ils apparaissent dans ce registre.

Contrôle des interruptions

Revenons maintenant d'un peu plus près à Exec. Pour mettre en place une interruption, il faut remplir devinez quoi ? Une structure, oui. Elle se nomme Interrupt et est définie ainsi :

interruptions

Le noeud "is_Node" permet à Exec de maintenir trier par ordre de priorité décroissante, toutes les interruptions. Il est de type NT_INTERRUPT. "is_Data" pointe sur... ce que vous voulez ! Ce pointeur sera transmis à la routine d'interruption. C'est l'idéal pour partager des variables et des données avec le programme principal. Enfin, "is_Code" pointe sur le code de la routine d'interruption elle-même.

Note : très peu de fonctions du système d'exploitation peuvent être appelées depuis une interruption. Notamment, toutes les fonctions faisant appel, directement ou indirectement, au gestionnaire de mémoire d'Exec sont à prohiber. En général, les fonctions suivantes sont utilisables sans danger depuis une interruption : Alert(), Disable(), Enable(), Signal(), Cause(), PutMsg(), RelPlyMsg(), FindPort(), FindTask().

Les gestionnaires

Comme on l'a déjà mentionné auparavant, un "handler" (gestionnaire) d'interruption est une routine qui gère exclusivement cette interruption. Il ne peut y avoir qu'un seul gestionnaire pour une interruption donnée.

Un gestionnaire est appelé par Exec comme s'il s'agissait d'une sous-routine avec une instruction JSR. La dernière instruction du gestionnaire doit donc être RTS, et non RTE. En entrée, certains registres du processeur contiennent des informations pertinentes : ce sont notamment A0, qui pointe sur l'adresse de base des puces spécialisées ($DFF000), A1 qui contient le champ is_Data de la structure Interrupt, A5 pointe sur la routine d'interruption elle-même (Exec effectue en effet un JSR (A5)) et A6 pointe sur ExecBase. Les registres D0-D1/A0-A1/A5-A6 sont librement modifiables, les autres doivent absolument être préservés. De plus, il appartient au gestionnaire d'effacer la demande d'interruption dans le registre matériel INTREQ.

On installe un gestionnaire à l'aide de la fonction SetIntVector(), qui s'utilise comme suit :

SetIntVector(ULONG IntNumber, struct Interrupt *interrupt);

"IntNumber" contient le numéro de l'interruption désirée, par exemple INTB_BLIT (voir "hardware/intbits.h" et "hardware/intbits.i"), et "interrupt" pointe sur votre structure Interrupt, correctement initialisée. Cette fonction retourne un pointeur sur la structure Interrupt du gestionnaire précédemment installé, s'il y en avait un. Il ne faudra pas oublier de remettre le vecteur dans son état initial lorsque votre programme se terminera, avec SetIntVector(IntNum, oldHandler).

Les serveurs

Contrairement aux gestionnaires, les serveurs d'interruptions se partagent une même interruption. Exec maintient une liste des serveurs déclarés pour chaque interruption, et les appelle tour à tour. Rappelons toutefois que seules les interruptions PORTS, COPER, VERTB, EXTER et NMI supportent des serveurs ; les autres requièrent obligatoirement un gestionnaire.

Avant d'appeler le serveur suivant dans la chaîne, Exec teste le drapeau Z du 68000 ; si ce drapeau n'est pas positionné, Exec arrête là le traitement de l'interruption. Un moyen simple de positionner le drapeau Z consiste à utiliser l'instruction MOVEQ :

MOVEQ #0,D0; Z est mis, la chaîne continue

Ou :

MOVEQ #1,D0; Z est faux, la chaîne s'arrête

Comme pour un gestionnaire, un serveur se termine obligatoirement par une instruction RTS.

Par contre, seuls les registres A1 et A5 contiennent une information valide : A1 contient le champ is_Data de la structure Interrupt et A5 pointe sur la routine d'interruption elle-même. Il ne faut pas s'attendre à trouver quoique ce soit de particulier dans A0 ni dans A6 (en fait, seul le premier serveur de la chaîne, celui qui a la plus haute priorité, reçoit effectivement l'adresse des puces spécialisées dans A0 et un pointeur sur ExecBase dans A6. Mais ces registres étant librement modifiables par le serveur, rien ne garantit aux suivants qu'ils seront encore valides).

Note : un bogue dans le serveur d'interruption VERTB de la graphics.library assume que quoiqu'il arrive, A0 pointe sur les puces spécialisées ($DFF000). La priorité de ce serveur est de 10. Donc, si vous ajoutez un serveur VERTB de priorité supérieure à 10, vous devez absolument terminer votre routine par les trois instructions :

interruptions

Enfin, les registres D0-D1/A0-A1/A5-A6 sont librement modifiables. Les autres doivent obligatoirement être préservés.

On installe un serveur d'interruption à l'aide de la fonction AddIntServer(), qui s'utilise comme suit :

AddIntServer(ULONG IntNum, struct Interrupt *interrupt);

Les paramètres sont les mêmes que pour SetIntVector(), à la différence qu'AddIntServer() ne retourne aucune valeur en réponse. Pour enlever votre serveur de la chaîne en fin de programme, utilisez RemIntServeur(IntNum, interrupt).

Listings

Je vous laisse le loisir de découvrir un petit programme de mon cru qui initialise et ajoute un serveur VERTB. Le programme principal est en C et la routine d'interruption, en assembleur.

Listing 1

/**********************************************
 * IntServ.c (v1.1)                           *
 * Met en place un serveur d'interruption VBL *
 *                                            *
 * S. Schreiber - 030590                      *
 *********************************************/

/*
 * Includes
 */
#include <stdio.h>
#include <stdlib.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <exec/interrupts.h>
#include <intuition/intuition.h>
#include <intuition/screens.h>
#include <graphics/gfx.h>
#include <hardware/intbits.h>

#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/graphics.h>

/*
 * Defines
 */
#define WIDTH 	640	/* Largeur de l'écran */
#define HEIGHT	128	/* Hauteur ''  ''     */
#define DEPTH	1	/* Profondeur  ''     */

/*
 * Types
 */
struct IntData
{	PLANEPTR id_Bitplane, id_Current;
	SHORT    id_BplSize,  id_BplCount;
};

/*
 * Variables globales
 */
struct IntuitionBase *IntuitionBase = NULL;
struct GfxBase *GfxBase = NULL;
struct Window *win = NULL;
struct Screen *scr = NULL;

/*
 * Données globales
 */
struct NewScreen ns =
{	0, HEIGHT, WIDTH, HEIGHT, DEPTH,
	0, 0,
	HIRES, CUSTOMSCREEN|SCREENQUIET,
	NULL, NULL, NULL, NULL
};

struct NewWindow nw =
{	0, 0, 640, 40,
	0, 1,
	CLOSEWINDOW|INTUITICKS,
	WINDOWCLOSE|WINDOWDRAG|WINDOWDEPTH|NOCAREREFRESH|ACTIVATE,
	NULL, NULL, "Fermez-moi pour terminer...", NULL, NULL,
	0, 0, 0, 0, WBENCHSCREEN
};

SHORT Palette[] = { 0x0000, 0x0AAA };

struct Interrupt VBLInt;
struct IntData intData;

/*
 * Prototypes
 */
extern void VBLServeur(void);	/* Ca, c'est le serveur */
void main(int argc, char **argv);
void cleanExit(UBYTE *str);

/*
 * C'est parti !
 */
void main(int argc, char **argv)
{
UBYTE buf[80];
int i;
struct IntuiMessage *im;

	if (!argc)	/* Lancement depuis le CLI seulement */
		exit(20);	/* (because on utilise printf)       */

	if (!(IntuitionBase = (struct IntuitionBase *)
	  OpenLibrary("intuition.library", 33L)))
		cleanExit("Pas d'Intuition !");

	if (!(GfxBase = (struct GfxBase *)
	  OpenLibrary("graphics.library", 33L)))
	  	cleanExit("Pas de Graphics !");

	if (!(win = OpenWindow(&nw)))
		cleanExit("Impossible d'ouvrir la fenêtre !");

	if (!(scr = OpenScreen(&ns)))
		cleanExit("Impossible d'ouvrir l'écran !");

	LoadRGB4(&scr->ViewPort, Palette, 1L << DEPTH);

/*
 * Remplissage de la structure qui sera transmise au serveur...
 */
	intData.id_Bitplane = scr->BitMap.Planes[0];
	intData.id_BplSize  = scr->BitMap.BytesPerRow * scr->BitMap.Rows;
	intData.id_Current  = intData.id_Bitplane;
	intData.id_BplCount = intData.id_BplSize;

	VBLInt.is_Node.ln_Type = NT_INTERRUPT;
	VBLInt.is_Node.ln_Name = "ANT";
	VBLInt.is_Node.ln_Pri  = 0;	/* Priorité nominale */
	VBLInt.is_Data         = (APTR)&intData;
	VBLInt.is_Code         = VBLServeur;
	AddIntServer(INTB_VERTB, &VBLInt);

	SetAPen(win->RPort, 1);
	SetDrMd(win->RPort, JAM2);
	Move(win->RPort, 10, 20);
	Text(win->RPort,
	     "Pendant que le serveur VBL s'amuse dans son écran...",
	     52);

	for(i = 0;;)
	{	WaitPort(win->UserPort);
		while (im = (struct IntuiMessage *)GetMsg(win->UserPort))
		{	switch(im->Class)
			{	case CLOSEWINDOW:
					RemIntServer(INTB_VERTB, &VBLInt);
					cleanExit(NULL);
					break;

				case INTUITICKS:
					sprintf(buf,
					        "Moi, je compte les INTUITICKS : %3d",
					        ++i);
					Move(win->RPort, 10, 30);
					Text(win->RPort, buf, 35);
					break;
			}
			ReplyMsg((struct Message *)im);
		}
	}
}

/*
 *
 */
void cleanExit(UBYTE *str)
{
	if (scr)
		CloseScreen(scr);

	if (win)
		CloseWindow(win);

	if (GfxBase)
		CloseLibrary((struct Library *)GfxBase);

	if (IntuitionBase)
		CloseLibrary((struct Library *)IntuitionBase);

	if (str)
		puts(str);

	exit(0);
}

Listing 2

	opt	c+,l+,o+,ow-

; ******************************
; * Serveur.s                  *
; * Serveur d'interruption VBL *
; * S. Schreiber - 030590      *
; ******************************

	XDEF	_VBLServeur	; On exporte ce label

; *****	Définition de notre structure IntData
	rsreset
IntData		rs.b	0
id_Bitplane	rs.l	1
id_Current	rs.l	1
id_BplSize	rs.w	1
id_BplCount	rs.w	1
id_SIZEOF	rs.w	0

; *****	Début du serveur
_VBLServeur:
	movea.l	id_Current(a1),a0	; prend l'adresse courante
	eori.l	#-1,(a0)+
	move.w	id_BplCount(a1),d0
	subq.w	#4,d0	; 1 mot long de moins
	beq.s	Reset
Retour	move.l	a0,id_Current(a1)
	move.w	d0,id_BplCount(a1)
	moveq	#0,d0	; Et la chaîne continue...
	rts

Reset	move.l	id_Bitplane(a1),id_Current(a1)
	move.w	id_BplSize(a1),id_BplCount(a1)
	moveq	#0,d0
	rts

Listing 3 (makefile pour IntServ)

EXE=IntServ
OBJ=IntServ.o Serveur.o
LIB=LIB:lc.lib LIB:amiga.lib
ARG=-b1 -cfist -v -y
LNK=SC SD DEFINE __main=__tinymain NOICONS ADDSYM

IntServ:     $(OBJ)
             BLink LIB:c.o $(OBJ) TO $(EXE) LIB $(LIB) $(LNK)

IntServ.o:   IntServ.c
             Lc $(ARG) IntServ.c

Interruptions générées par Exec

Les différentes interruptions que nous avons vues jusqu'à présent étaient toutes générées par Paula, suite à un évènement matériel. Mais Exec permet également la génération d'interruptions totalement logicielles.

On ne voit pas très bien à priori à quoi des interruptions logicielles peuvent bien servir... Alors qu'il est souvent primordial de pouvoir réagir au quart de millionième de seconde près au VBL ou à la fin d'une transmission série, un petit délai (dû à un grand nombre de tâches tournant concurremment) dans un protocole de communication entre deux processus n'est que très rarement pénalisant. En fait, c'est justement là leur utilité : permettre de synchroniser le plus justement possible deux tâches entre elles.

Le principe est très simple : étant donné que les messages sont chaînés les uns à la suite des autres dans un port, un message particulier peut mettre un certain temps avant d'être traité par la tâche réceptrice. Or, dans certains cas, il est primordial que ce message soit immédiatement pris en compte, quitte à ce que les autres patientent un peu, bien au chaud qu'ils sont dans leur liste d'attente. Pour ce faire, on décide de suspendre provisoirement le système multitâche - grâce à une interruption, donc - et de gérer tout de suite l'évènement intervenu. On le voit, les interruptions logicielles agissent exactement comme les interruptions matérielles, à ceci près qu'elles sont générées non pas par le matériel, mais par le logiciel (en fait, elles utilisent le niveau d'interruption 1, bit 2 de INTENA).

Mise en oeuvre d'une interruption logicielle

Une interruption logicielle s'initialise exactement de la même manière qu'une interruption matérielle, au moyen d'une structure Interrupt correctement remplie. Attention ici à bien spécifier dans le noeud, le type NT_INTERRUPT et non NT_SOFTINT, qui est un drapeau interne à Exec. Seules cinq priorités sont possibles : -32, -16, 0, +16 et +32 ; toute autre valeur est illégale. Les champs is_Code et is_Data sont initialisés comme pour une interruption matérielle.

Une fois déclenchée, l'interruption logicielle s'exécute dans un environnement identique à celui des interruptions matérielles : mode superviseur, retour au gestionnaire d'Exec par l'instruction RTS et protection obligatoire des registres A2-A4 et D2-D7. Si elle survient en cours de fonctionnement "normal" du système (mode multitâche), l'interruption est exécutée immédiatement ; sinon, si elle est déclenchée au cours d'une autre interruption logicielle, elle devra attendre que celle-ci se termine ; enfin, si elle est déclenchée au cours d'une interruption matérielle, elle devra attendre que tous les serveurs éventuels restant à exécuter aient fini leur travail. Ceci permet par exemple à une interruption de haut niveau de se décharger d'une tâche ingrate et gourmande en temps-machine sur une autre interruption de priorité moindre.

Il existe deux moyens de déclencher une interruption logicielle : soit en appelant la fonction Cause(), soit en postant un message dans un port de type PA_SOFTINT.

Dans le premier cas, on transmet à Cause() l'adresse de la structure Interrupt concernée et l'interruption se déclenche suivant les conditions énoncées plus haut. Dans le second cas, on initialise un MsgPort comme illustré ci-dessous :

Interruptions
Interruptions

Ensuite, chaque message envoyé sur ce port déclenchera l'interruption logicielle concernée. Notez qu'en assembleur, il existe un champ supplémentaire dans la structure MsgPort, baptisé MP_SOFTINT, qui permet de différencier à l'écriture du programme un port d'interruption d'un port de message. Sa valeur est égale à celle de MP_SIGTASK.

Emploi avec les périphériques logiciels

Comme les interruptions matérielles, les interruptions logicielles ne peuvent appeler qu'un nombre réduit de fonctions du système. Une particularité est à noter concernant les devices : normalement, il est interdit d'utiliser DoIO() et BeginIO() dans une routine d'interruption, mais le timer.device et l'audio.device font exception à cette règle. Cela permet par exemple de relancer directement une requête de l'un de ces devices depuis l'interruption, sans que le programme principal n'ait besoin d'intervenir.

L'exemple

Comme le veut la tradition, nous allons terminer cette étude des interruptions logicielles par un petit exemple. En l'occurrence, il ne s'agit ni plus ni moins que d'un petit compteur de 1/2 secondes. On aurait certes pu le programmer d'une autre manière, mais c'est la seule idée de programme qui me soit passé par la tête...

;
; Exemple (tout-à-fait inutile) d'interruption logicielle.
;
; L'interruption compte les 1/2 secondes...
; Le programme principal attend simplement un CTRL/C puis
; affiche le résultat du comptage.
;
; S. Schreiber - 171190
;

	incdir	"include:"
	include	"exec/interrupts.i"
	include	"devices/timer.i"
	include	"libraries/dos.i"

	include	"exec/exec_lib.i"
	include	"intuition/intuition_lib.i"
	include	"libraries/dos_lib.i"

OFF	EQU	0	; Compteur OFF
ON	EQU	1	; Compteur ON
STOP	EQU	2	; Compteur arrêté

DELAI	EQU	50000	; Nb micro-secondes (1/2 seconde)

; ************************************
Start	lea	dosname(pc),a1	; Ouvre la dos.library
	moveq	#0,d0
	CALLEXEC OpenLibrary
	move.l	d0,_DOSBase
	beq	NoDos

	CALLDOS	Output		; Pour communiquer...
	move.l	d0,stdout

	move.l	d0,d1		; Salut !
	move.l	#text,d2
	moveq	#tlen,d3
	CALLDOS	Write

	lea	tireq(pc),a1	; Ouvre le timer.device
	move.b	#NT_MESSAGE,IO+MP+LN_TYPE(a1)
	move.l	#SoftIntPort,IO+MN_REPLYPORT(a1)
	lea	tiname(pc),a0
	moveq	#UNIT_MICROHZ,d0
	moveq	#0,d1
	CALLEXEC OpenDevice
	tst.b	d0
	bne	NoTimer

	lea	SoftIntPort(pc),a1
	CALLEXEC AddPort

	bsr	begintr		; Lance la 1ère requête...

*******	Programme principal : attente de CTRL/C (!)

Main	move.l	#SIGBREAKF_CTRL_C,d0
	CALLEXEC Wait

******	C'est tout !!!

	move.l	counter(pc),longout

	move.b	#OFF,iflag	; Demande à l'interruption de
Exit	cmpi.b	#STOP,iflag	; s'arrêter et attend qu'elle
	bne.s	Exit		; le fasse

	lea	nbfmt(pc),a0	; Affiche le compteur
	lea	counter(pc),a1
	lea	putche(pc),a2
	lea	charout(pc),a3
	CALLEXEC RawDoFmt

	lea	SoftIntPort(pc),a1
	CALLEXEC RemPort

	lea	tireq(pc),a1	; Ferme le timer.device
	CALLEXEC CloseDevice

NoTimer	movea.l	_DOSBase(pc),a1	; Ferme la dos.library
	CALLEXEC CloseLibrary

NoDos	moveq	#0,d0		; Retour au CLI
	rts

; ************************************
putche	movem.l	d0-d3/a6,-(sp)	; J'avais pas envie de bufferiser !
	move.b	d0,(a3)
	move.l	stdout(pc),d1
	move.l	a3,d2
	moveq	#1,d3
	CALLDOS	Write
	movem.l	(sp)+,d0-d3/a6
	rts

; ************************************
; *****	Routine d'interruption logicielle
SoftCode:
	addq.l	#1,counter	; Incrémente le compteur
	cmpi.b	#ON,iflag	; On s'arrête ?
	beq.s	begintr		; Pas encore, relance la requête
	move.b	#STOP,iflag	; Oui, prévient Main
	rts

;
; Lancement d'une requête timer pour DELAI micro-secondes.
;
; ATTENTION :
; -----------
; Ca ne marche qu'avec le timer.device ou l'audio.device !
; On ne peut pas appeler les autres devices depuis une
; interruption, même logicielle !
;
begintr	lea	tireq(pc),a1
	move.w	#TR_ADDREQUEST,IO_COMMAND(a1)
	move.l	#DELAI,IOTV_TIME+TV_MICRO(a1)
	BEGINIO
	rts

; ************************************
; Variables
_DOSBase dc.l	0	; dos.library
stdout	dc.l	0	; fenêtre CLI
counter	dc.l	0	; compteur
longout	dc.l	0	; buffer pour RawDoFmt
charout	dc.b	0	; ""
iflag	dc.b	ON	; flag d'arrêt

; MsgPort de type PA_SOFTINT
SoftIntPort:
	dc.l	0,0		; ln_Succ, ln_Pred
	dc.b	NT_MSGPORT,0	; ln_Type, ln_Pri
	dc.l	pName		; ln_Name
	dc.b	PA_SOFTINT,0	; mp_Flags, mp_SigBit
	dc.l	SoftInt		; mp_SigTask
	dcb.b	LH_SIZE		; mp_MsgList

; structure Interrupt
SoftInt	dc.l	0,0		; ln_Succ, ln_Pred
	dc.b	NT_INTERRUPT,0	; ln_Type, ln_Pri
	dc.l	siName		; ln_Name
	dc.l	0,SoftCode	; is_Data, is_Code

tireq	dcb.b	IOTV_SIZE	; IORequest pour le timer.device

dosname	DOSNAME
tiname	TIMERNAME
pName	dc.b	"Horloge.Port",0
siName	dc.b	"Horloge.interrupt",0
text	dc.b	"L'interruption logicielle compte les demi-"
	dc.b	"secondes...",10,"Pressez CTRL/C pour arrêter.",10
tlen	EQU	*-text

nbfmt	dc.b	"Résultat du comptage : %ld",10,0
	even

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


[Retour en haut] / [Retour aux articles]