amiga-news ENGLISH VERSION
.
Links| Forum| Kommentare| News melden
.
Chat| Umfragen| Newsticker| Archiv
.

amiga-news.de Forum > Programmierung > Registerzugriff in C [ - Suche - Neue Beiträge - Registrieren - Login - ]

1 -2- 3 4 [ - Beitrag schreiben - ]

26.10.2007, 20:35 Uhr

jolo
Posts: 110
Nutzer
@whose:

Hi.

Zitat:
Also, ich hab das 1. AudioGrabber gerade mal durch den vbcc gescheucht (Cubic-Installation), Compilerlauf war völlig problemlos (keine Warnungen oder Fehler).

Pweeh. Und ich hatte schon die Befürchtung, dass ich etwas falsch bei den manuellen Installationen gemacht hätte. :)

Zitat:
Groß auf Funktion testen konnte ich es mangels Sampler allerdings nicht ;-)

Da seit Jahren mein A4000 im Keller verbannt ist, kann ich es auch nicht mehr testen...
Und meinen Digitizer an den PC anschließen - hmmm, nicht mein Ding. :}


Zitat:
Ein Knackser zum Start dürfte aber darauf hinweisen, daß es versucht, Audio abzuspielen. Beenden ließ es sich mit Break-C ebenfall problemlos.

Ja, das Knacksen bedeutet, dass Daten an die Harware transferiert werden. Müsste aber schnell vorbei sein - es werden ja keine unterschiedliche Werte gelesen.


Zitat:
Nachtrag: Es scheint aber die Sound-Emulation von WinUAE zu beeinflussen, der Windows-Sound klingt reichlich verbogen nach Start des Audio-Grabber. Eine Ahnung, was dafür verantwortlich sein könnte?

Das ist ein Fehler von WinUAE bzw. Windows-XP. Auch ohne AudioGrabber passiert dies bei mir hin und wieder. Meistens reicht es aus, WinUAE neu zu starten. Wenn's dann noch nicht klappt, PC neu starten...
Entweder dies ist ein Initialisierungsfehler von WinUAE oder es hat mit dem unzureichenden Timing unter XP zu tun.
Windows ist nicht gerade ein Timing-Weltmeister.


Zitat:
Sehr schönes Beispiel für Interrupts in C übrigens, vielen Dank dafür!

Man sollte den Code mit Vorsicht genießen - er ist dermaßen hardwarenah geschrieben, dass der Interrupt weder unter Amithlon, OS4 noch MorphOS zum Laufen gebracht werden kann. Erstens gibt es dort keine CIAs, noch ist "poken" dort erlaubt.
Da allerdings der parallele Port direkt ausgelesen wurde, gehe ich davon aus, dass die Hardwarevoraussetzungen für diesen Typ von Interrupt erfüllt sind.

Ein schönes Beispiel, in meinen Augen, folgt jetzt.
Dieses benutze ich in abgewandelter Form in echten Applikationen.
Sollte unter OS4, Amithlon und MorphOS auch laufen.
Es ist ein Level-1 Interrupt - somit können gefahrlos andere Interrupts von diesem aus aufgerufen werden. Zudem wird selbst bei rechenintensivem Code innerhalb des Interrupts das System nicht schwerwiegend beeinflusst, da ja alle systemkritischen Interrupts mit einer höheren Priorität arbeiten. Nur Prozesse laufen mit einer niedrigeren Priorität.


code:
/* vc -c99 -cpu=68020 -O1 -sc -sd TimerIRQ.c -o TimerIRQ -lvcs -lamigas */

#include <exec/memory.h>
#include <exec/interrupts.h>
#include <exec/ports.h>
#include <exec/io.h>

#include <dos/dos.h>
#include <dos/dosextens.h>

#include <devices/timer.h>

/* Beide müssen schon geöffnet sein (Startup-Code)... */
extern struct DosLibrary *DOSBase;
extern struct ExecBase *SysBase;

#define __NOLIBBASE__	/* Wir benötigen keine anderen! */

#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/timer.h>

#include <clib/alib_protos.h>


/* Wir müssen Argumente in Registern handhaben */
#ifdef __GNUC__
 #define REG( reg,arg) arg __asm( #reg)
#else
 #ifdef __VBCC__
  #define REG( reg,arg) __reg(#reg) arg
 #else
  #define REG( reg,arg) register __##reg arg
 #endif
#endif


/* MaxonC++/HisoftC++ verstehen kein __saveds - also großes Datenmodell
   verwenden! */
#ifdef __MAXON__
#define __saveds
#endif

/* Interrupt basierend */
#if defined(__SASC) || defined(__STORM__) || defined(__GNUC__)
 #define SUPERIOR __interrupt __saveds
#else
 #if defined(__VBCC__)
 #define SUPERIOR __amigainterrupt __saveds
 #else
   #define SUPERIOR __saveds	/* MaxonC++/HiSoftC++ */
 #endif
#endif


/* Diese drei Flaggen beherrscht unser Interrupt */
#define KILL   -1
#define ALIVE	1
#define KILLED	0

struct TimeOutIRQData
{
	/* Alles Interrupt private Daten - Hände weg! */
	struct MsgPort	 	*TimeOutPort;
	struct Interrupt	*TimeOutIRQ;
	struct timerequest	*TimeOutRequest;
	struct Task			*TimeOutTask;
	ULONG				TimeOutDelay;
	LONG				TimeOutFlag;
	ULONG				TimeOutSignal;
	/* Zeiger auf Benutzer spezifizierte Funktion */
	LONG				(*IRQUsersCode)(APTR);
	/* Zeiger auf Benutzer spezifizierte Daten (Struktur?) */
	APTR				IRQUsersData;
	/* Falls ungleich null, wird ein Signal gesendet */
	LONG				SendSignal;
};



/* Diese Routine wird innerhalb eines Cause()-Aufrufs ausgeführt! */
SUPERIOR LONG IRQCodeEntry( REG(a1, struct TimeOutIRQData *tid) )
{
	struct timerequest *tr;
	LONG i;

	i = 2;

	/* Entferne nur die Nachricht vom Port */
	tr = (struct timerequest *) GetMsg( tid->TimeOutPort);

	/* Während wir (kurzzeitg) leben, können wir dem Hauptprogramm ein Signal geben... */
	if ( (tr) && (tid->TimeOutFlag == ALIVE) )
	{
		/* Falls Benutzer-Code vorhanden, diesen jetzt anspringen */
		if (tid->IRQUsersCode)
			tid->SendSignal = (LONG) tid->IRQUsersCode( (APTR) tid->IRQUsersData);

		/* Falls SendSignal != 0 wird Signal gesendet! */
		if (tid->SendSignal)
			Signal( tid->TimeOutTask, (1 << tid->TimeOutSignal) );

		/* Mit diesem Selbsterhaltenden Trick rufen wir uns in bestimmten
		   Zeitabständen selber auf!
		   NB: BeginIO() darf nur in Verbindung mit den Timer- und
		   Audio-Devices innerhalb eines Interrupts verwendet werden. */
		tr->tr_node.io_Command = TR_ADDREQUEST;
		tr->tr_time.tv_micro = tid->TimeOutDelay;
		BeginIO( (struct IORequest *) tr);
	}
	else	/* Upps, wir sollen Selbstmord begehen... */
	{
		/* Sage Hauptprogramm, dass wir's nicht geschafft haben... ;-) */
		tid->TimeOutFlag = KILLED;
	}

	return i - 2;
}

/*
 * Initialisiere einen Interrupt:
 * @callsPerSecond:	Wie oft pro Sekunde soll ein Interrupt generiert werden?
 * @irqname:		Der Name anhand dessen der Interrupt identifiziert werden
 *					kann.
 * @usersCode:		Routine, die im Interrupt abgearbeitet werden soll - darf
 *					natürlich auch NULL sein - dann wird halt nichts anderes
 *					gemacht, als in bestimmten Zeitabständen ein Signal an
 *					das Hauptprogramm zu senden.
 *					Falls usersCode spezifiziert wurde, wird nur dann ein
 *					Signal ans Hauptprogramm gesendet, wenn diese Routine
 *					einen Wert ungleich null zurück gibt (Returncode).
 * @usersData:		Zeiger auf eine Variable oder Struktur die usersCode
 *					empfängt (m68k Stack, PPC Register)
 */

struct TimeOutIRQData *InitIRQ( ULONG callsPerSecond,  STRPTR irqname,
				LONG (*usersCode)(), APTR usersData)
{
	struct TimeOutIRQData *tid;

	/* Globale Struktur, die mit dem Interrupt geteilt wird */
	if ( (tid = (struct TimeOutIRQData *)
		 AllocVec( sizeof (struct TimeOutIRQData),
	 		   MEMF_PUBLIC | MEMF_CLEAR)) )
	{
		/* Für einen MessagePort, benutzt von einem Interrupt, benötigen
		   wir kein Signal - also CreatePort() und/oder CreateMsgPort()
		   nicht benutzen! */
		if ( (tid->TimeOutPort = (struct MsgPort *)
					  AllocVec( sizeof (struct MsgPort),
		  			MEMF_PUBLIC | MEMF_CLEAR)) )
		{
			/* Initialisiere Message-Liste */
			NewList( &(tid->TimeOutPort->mp_MsgList));

			/* Die Interrupt-Struktur selber... */
			if ( (tid->TimeOutIRQ = (struct Interrupt *)
						AllocVec( sizeof (struct Interrupt),
								  MEMF_PUBLIC | MEMF_CLEAR)) )
			{
				/* Software-Interrupts dürfen nur Prioritäten mit Werten
				   von -32, -16, 0, +16, +32 besitzen - und der korrekte
				   Typ für Software-Interrupts ist NT_INTERRUPT */
				tid->TimeOutIRQ->is_Node.ln_Name = irqname;	/* Brandmarke... */

				/* Je niedriger, desto besser... */
				tid->TimeOutIRQ->is_Node.ln_Pri = -32;

				/* Software-Interrupt's Einstieg */
				tid->TimeOutIRQ->is_Code = (void (*)()) IRQCodeEntry;

				/* Wir übergeben eigene Struktur */
				tid->TimeOutIRQ->is_Data = tid;

				/* Es ist ein MessagePort */
				tid->TimeOutPort->mp_Node.ln_Type = NT_MSGPORT;

				/* ...und wird von einem Interrupt verwendet. */
				tid->TimeOutPort->mp_Flags = PA_SOFTINT;

				/* Interrupt's Adresse */
				tid->TimeOutPort->mp_SigTask = (struct Task *) tid->TimeOutIRQ;

				/* Einen "TimeRequest" initialisieren. */
				if ( (tid->TimeOutRequest = (struct timerequest *)
						CreateExtIO( tid->TimeOutPort, sizeof (struct timerequest))) )
				{
					/* Öffne Timer-Device. NULL heißt Zugriff! */
					if ( !(OpenDevice( "timer.device",
							UNIT_MICROHZ,
							(struct IORequest *) tid->TimeOutRequest,
							0)))
					{
						/* Wenn der Interrupt auf diese Flagge stößt, weiß
						   er, dass er laufen darf */
						tid->TimeOutFlag = ALIVE;

						/* Das Signal, auf welches das Hauptprogramm zu
						   reagieren hat... */
						tid->TimeOutSignal = AllocSignal( -1L);

						if (tid->TimeOutSignal != -1L)
						{
							/* Lass den Interrupt wissen, an wen das betreffende
							   Signal gesendet werden muss */
							tid->TimeOutTask = FindTask( NULL);

							/* Wie oft pro Sekunde soll ein Interrupt generiert
							   werden? */
							tid->TimeOutDelay = 1000000 / callsPerSecond;

							/* Der Benutzer-Code */
							tid->IRQUsersCode = (LONG (*)(APTR)) usersCode;

							/* ...und die Daten, die der Code empfängt */
							tid->IRQUsersData = usersData;

							/* Standardmäßig wird bei jedem generierten
							   Interrupt ein Signal gesendet */
							tid->SendSignal = 1;

							return tid;	/* Alles in Butter! */
						}
						CloseDevice( (struct IORequest *) tid->TimeOutRequest);
					}
				}
				DeleteExtIO( (struct IORequest *) tid->TimeOutRequest);
			}
			FreeVec( tid->TimeOutIRQ);
		}
		FreeVec( tid->TimeOutPort);
	}
	FreeVec( tid);

	return NULL;	/* Irgendein Fehler... */
}

/*
 * Nach der Initialisierung wollen wir natürlich auch den Interrupt
 * starten.
 */
void StartIRQ( struct TimeOutIRQData *tid)
{
	/* Das Kommando, welches wir benutzen müssen... */
	tid->TimeOutRequest->tr_node.io_Command = TR_ADDREQUEST;

	/* Der Wert, nachdem ein Interrupt generiert wird. */
	tid->TimeOutRequest->tr_time.tv_micro = tid->TimeOutDelay;

	/* Starte Interrupt. */
	BeginIO( (struct IORequest *) tid->TimeOutRequest);
}


/*
 * Interrupt und Ressourcen freigeben.
 */
void DeleteIRQ( struct TimeOutIRQData *tid)
{
	tid->TimeOutFlag = KILL;

	/* Spiele nicht mit einem aktiven Interrupt herum... */
	/* ...das System könnte ärgerlich reagieren (GURU) */
	while (tid->TimeOutFlag != KILLED)
		Delay( 5);

	FreeSignal( tid->TimeOutSignal);
	CloseDevice( (struct IORequest *) tid->TimeOutRequest);
	DeleteExtIO( (struct IORequest *) tid->TimeOutRequest);
	FreeVec( tid->TimeOutIRQ);
	FreeVec( tid->TimeOutPort);
	FreeVec( tid);
}


/*
 * Damit die Struktur privat bleibt, müssen wir eine Funktion einführen,
 * damit man das Signal-Bit auslesen kann.
 */
ULONG GetIRQsSignalBit( struct TimeOutIRQData *tid)
{
	return tid->TimeOutSignal;
}


/*
 * Kleines Gimmick - wir können dem Interrupt auch eine neue Zeitspanne
 * zuweisen:
 */
void SetNewIRQDelay( struct TimeOutIRQData *tid, ULONG callsPerSecond)
{
	/* Wie oft pro Sekunde soll ein Interrupt generiert werden? */
	tid->TimeOutDelay = 1000000 / callsPerSecond;
}


/* Hauptprogramm */
int main( int argc, char **argv)
{
	struct TimeOutIRQData *tid;
	BOOL alive;
	ULONG received, counter;

	/* Bitte, keine Werte größer 15 verwenden - I/O Operationen (VFPrintf) sind langsam... */
	tid = InitIRQ( 15, "15-mal pro Sekunde IRQ", NULL, NULL);
	if (tid)
	{
		alive = TRUE;
		counter = 0;

		StartIRQ( tid);

		while (alive)
		{
			counter ++;

			received = Wait( SIGBREAKF_CTRL_C | (1 << GetIRQsSignalBit( tid)) );
			if (received & SIGBREAKF_CTRL_C)
				alive = FALSE;
			else
				VFPrintf( Output(),
					  "Signal vom Interrupt eingetroffen, zum %ld-mal!n",
					  (CONST APTR) &counter );
		}

		DeleteIRQ( tid);
	}

	return 0;
}


Das schöne an diesem Beispiel ist, dass die gesamte Implementierung abstrahiert werden kann.
Nachdem die betreffenden Routinen einmal als Objekt-Datei (Link-Lib) existieren, könnte man den Datentyp unkenntlich machen, so dass keine Struktur und auch kein Struktureintrag sichtbar ist, z.B. durch Definition von "Object *" anstelle von "struct TimeOutIRQData *".
Das hätte zur Folge, dass der Nutzer nur seine Daten/Code sieht und natürlich die Funktionen, die nach außen geführt wurden.
Einfacher geht es kaum.

Ach ja, man hätte auch eine eigene Funktion spezifizieren können, etwa so:

code:
LONG MyCode( ULONG *cnt)
{
	*cnt = *cnt + 1;
	if (*cnt % 15)
		return 0;
	else
		return 1;	/* Nur jeder 15. Aufruf veranlasst uns, ein Signal zu
					   senden (jede halbe Sekunde) */ 
}


/* Hauptprogramm */
int main( int argc, char **argv)
{
	struct TimeOutIRQData *tid;
	BOOL alive;
	ULONG received, counter;

	tid = InitIRQ( 30, "30-mal pro Sekunde IRQ", (LONG (*)()) &MyCode, &counter);
	if (tid)
	{
		alive = TRUE;
		counter = 0;

		StartIRQ( tid);

		while (alive)
		{
			received = Wait( SIGBREAKF_CTRL_C | (1 << GetIRQsSignalBit( tid)) );
			if (received & SIGBREAKF_CTRL_C)
				alive = FALSE;
			else
				VFPrintf( Output(),
					  "Signal vom Interrupt eingetroffen, Zähler = %ld.n",
					  (CONST APTR) &counter );
		}

		DeleteIRQ( tid);
	}

	return 0;
}



Wenn ich ganz ehrlich bin, gefällt mir dieser Interrupt-Typ wesentlich besser. :)

Gruß

[ - Antworten - Zitieren - Direktlink - ]

26.10.2007, 20:54 Uhr

Holger
Posts: 8116
Nutzer
Zitat:
Original von jolo:
Anbei, für Zitate verwendet man
Zitat:
und
. Steht zwar nirgendwo beschrieben, funktioniert aber.

Steht nicht beschrieben, sieht man aber, wenn man einmal auf "Zitieren" geklickt hat.

mfg

--
Good coders do not comment. What was hard to write should be hard to read too.

[ - Antworten - Zitieren - Direktlink - ]

26.10.2007, 22:50 Uhr

MaikG
Posts: 5172
Nutzer
>Ohne abgeschaltetes Multitasking wirst Du generell Schwierigkeiten
>haben, Deinen Prozess (nicht Interrupt!) in solch kurzen Intervallen
>eine Aktion ausführen zu lassen.


Mit einer for next schleife um zeit zu verschwenden sind 18000 HZ
kein Thema, weder auf 030 noch auf 060. Ich nehme auch forbid/permit
damit nichts anderes dazwischen pfuscht...


>Nochmals, der Interrupt läuft ständig - und füllt zwei Puffer,

Ja, das Funktionsprinzip des Programms hab ich doch verstanden.


>Hmmm, aber irgendwo muss der Hase doch begraben sein?

Sind zu viele Functions, zu unübersichtlich zu lang.
Es dauert halt bis ich das begreife.
Ausserdem muss ich sachen übersetzten die ich überhaupt
nicht kenne.

>Das Beispiel ist wirklich primitiv.

Nicht für mich.


>Den Quelltext habe ich damals unter Devpac erstellt - hast Du den?
>Ansonsten kann es schwer werden, diesen zu assemblieren.

Assembler ist doch Assembler. Das sind Hexdezimale Nummern in
Menschlich lesbaren Format dargestellt.
Ausser ggf. Optionen und Kommentaren ist es doch alles das selbe.
Ich hab ASM-Pro.


[ - Antworten - Zitieren - Direktlink - ]

26.10.2007, 22:54 Uhr

MaikG
Posts: 5172
Nutzer
>Pweeh. Und ich hatte schon die Befürchtung, dass ich etwas falsch
>bei den manuellen Installationen gemacht hätte. :)

Hab auch eine 2. Installation (WarpUP) Version getestet,
geht auch nicht sind aber andere Fehlermeldungen.



>Man sollte den Code mit Vorsicht genießen - er ist dermaßen
>hardwarenah geschrieben, dass der Interrupt weder unter Amithlon,
>OS4 noch MorphOS zum Laufen gebracht werden kann.
>Erstens gibt es dort keine CIAs, noch ist "poken" dort erlaubt.

OS4 kommt bald für Classic, dann gibts CIA's.

[ - Antworten - Zitieren - Direktlink - ]

27.10.2007, 10:57 Uhr

DrNOP
Posts: 4118
Nutzer
Zitat:
Original von Holger:
Steht nicht beschrieben, sieht man aber, wenn man einmal auf "Zitieren" geklickt hat.

Ja, ich frag' mich schon seit jeher, wie Leute es schaffen das anders zu machen. Wenn ich alten Text will "Zitieren" drücken, der Text ist da und auch schon formatiert. Ich versteh's nicht, wie man das ohne Knoten im Gehirn umgehen kann... I-) :D
--
Signaturen mit mehr als zwei Zeilen gehen mir auf den Wecker

[ - Antworten - Zitieren - Direktlink - ]

27.10.2007, 14:22 Uhr

whose
Posts: 2156
Nutzer
Salve! ;)

Zitat:
Original von jolo:

Zitat:
Groß auf Funktion testen konnte ich es mangels Sampler allerdings nicht ;-)

Da seit Jahren mein A4000 im Keller verbannt ist, kann ich es auch nicht mehr testen...
Und meinen Digitizer an den PC anschließen - hmmm, nicht mein Ding. :}


Ich schätze mal, es funktioniert. Sound wird auf jeden Fall "ausgegeben", auch wenn ich hier (mangels Daten) nichts höre.

Zitat:
Zitat:
Ein Knackser zum Start dürfte aber darauf hinweisen, daß es versucht, Audio abzuspielen. Beenden ließ es sich mit Break-C ebenfall problemlos.

Ja, das Knacksen bedeutet, dass Daten an die Harware transferiert werden. Müsste aber schnell vorbei sein - es werden ja keine unterschiedliche Werte gelesen.


Ein "Knackser" ist per (Audiophilen-) Definition kurz ;) Aber richtig, danach ist Ruhe.

Zitat:
Zitat:
Nachtrag: Es scheint aber die Sound-Emulation von WinUAE zu beeinflussen, der Windows-Sound klingt reichlich verbogen nach Start des Audio-Grabber. Eine Ahnung, was dafür verantwortlich sein könnte?

Das ist ein Fehler von WinUAE bzw. Windows-XP. Auch ohne AudioGrabber passiert dies bei mir hin und wieder. Meistens reicht es aus, WinUAE neu zu starten. Wenn's dann noch nicht klappt, PC neu starten...
Entweder dies ist ein Initialisierungsfehler von WinUAE oder es hat mit dem unzureichenden Timing unter XP zu tun.
Windows ist nicht gerade ein Timing-Weltmeister.


Ok, dann weiß ich mehr. Es reicht bei mir, nach dem Break-C die WinUAE-Konfiguration mit F12 zu aktivieren, um den verbogenen Windows-Sound wieder "geradezubiegen".


Zitat:
Zitat:
Sehr schönes Beispiel für Interrupts in C übrigens, vielen Dank dafür!

Man sollte den Code mit Vorsicht genießen - er ist dermaßen hardwarenah geschrieben, dass der Interrupt weder unter Amithlon, OS4 noch MorphOS zum Laufen gebracht werden kann. Erstens gibt es dort keine CIAs, noch ist "poken" dort erlaubt.
Da allerdings der parallele Port direkt ausgelesen wurde, gehe ich davon aus, dass die Hardwarevoraussetzungen für diesen Typ von Interrupt erfüllt sind.


Ja gut, auf die evtl. nicht vorhandenen CIAs sollte man natürlich achten. Aber grundsätzlich war es ein schönes Beispiel für Interrupts in C mit Bezug zur Praxis. Ich denke nicht, daß SoftInterrupts so häufig Verwendung finden, es sei denn, man möchte dafür Sorge tragen, daß einen das Multitasking nicht bei zeitkritischen Aufgaben ausbremst. Hardware-Interrupts sind meiner Meinung nach eigentlich häufiger anzutreffen (u.A. für die CIA-Timer).

Zitat:
Ein schönes Beispiel, in meinen Augen, folgt jetzt.
Dieses benutze ich in abgewandelter Form in echten Applikationen.
Sollte unter OS4, Amithlon und MorphOS auch laufen.
Es ist ein Level-1 Interrupt - somit können gefahrlos andere Interrupts von diesem aus aufgerufen werden. Zudem wird selbst bei rechenintensivem Code innerhalb des Interrupts das System nicht schwerwiegend beeinflusst, da ja alle systemkritischen Interrupts mit einer höheren Priorität arbeiten. Nur Prozesse laufen mit einer niedrigeren Priorität.


Wenn ich ehrlich bin muß ich sagen, daß ich dieses Beispiel für Anfänger wesentlich schwerer zu durchschauen finde, weil dort nicht so wirklich klar wird, auf welche Weise der SoftInt "aufgerufen" wird bzw. was diesen Interrupt eigentlich auslöst. Es wird auch nicht ganz klar, welchen Nutzen man daraus ziehen kann, daß "andere Interrupts" davon aufgerufen werden können.

Für die Vorstellung Vieler dürfte ein Interrupt etwas "im Wesen deutlich verschiedenes" zu Tasks/Prozessen sein, diese Beispiel zeigt aber im Grunde (korrigiere mich, wenn ich da falsch liege) einen Task, der teilweise aus dem "normalen" Multitasking rausgenommen und auf Interrupt-Ebene verlagert wurde.

Ich denke nicht, daß ein Unerfahrerener so ohne weiteres kapiert, daß dieser "Interrupt" quasi wie ein normaler Task via Signal vom timer.device "aufgeweckt" wird (was ja keineswegs der landläufigen Vorstellung von einem Interrupt entspricht).

Da war das CIA-abhängige Beispiel schon etwas informativer bzw. einfacher zu begreifen, finde ich.

Ok, MaikG hat noch so seine Probleme mit dem Verständnis, das liegt aber wohl eher an mangelnder Erfahrung mit C und mit dem System AmigaOS (er hackt ganz gern ;) ).

Ich fand das erste Beispiel jedenfalls schön praxisbezogen und gut dokumentiert, Abstraktion wie im letzten Beispiel ist eigentlich ein Thema für fortgeschrittenere Programmierer und für seine Zwecke auch gar nicht wirklich nötig (mal davon abgesehen daß ihm derzeit noch das Verständnis für Blackboxes fehlt)

Mit etwas mehr Erläuterungen, zum Beispiel wofür sowas wie ein SoftInt eigentlich gut ist und wie man den, außer in Verbindung mit dem timer.device, in der Praxis nutzt, ist es aber sicherlich auch ein sehr schönes Beispiel für Interrupts in C :)

Grüße

--
---

:boing: µA1 PPC 750GX-800
:boing: A4000 PPC 604e-233

[ - Antworten - Zitieren - Direktlink - ]

27.10.2007, 18:52 Uhr

jolo
Posts: 110
Nutzer
@MaikG:

Hi.

Zitat:
Das Beispiel ist wirklich primitiv.

Nicht für mich.



Tschuldigung, aber ich glaube Du machst Dich nur selber bange.

Also, erstmal relaxen! :)

Das erste Buch, das ich zum Thema Programmierung gelesen habe, fing mit diesen Worten an:
"Wenn Sie bei Bit nicht mehr an Bier denken, sind Sie auf dem richtigen Weg!"

Als ich mich mit C++ beschäftigte, sprangen zwei freundliche Worte mir entgegen:
"Don't panic!"

Das solltest Du auch beherzigen. :)

Letztendlich ist die Interrupt-Programmierung auf einem Amiga nicht komplizierter als der Rest, den Du mittels Deiner favorisierten Programmiersprache und den Betriebssystemroutinen erzielen kannst.

Ich gestehe, dass Du es schwieriger hast als andere, da Du keine fehlerfreie Compiler-Umgebung besitzt und dementsprechend auch nicht die Beispielprogramme übersetzen kannst, so dass Du letztendlich diese nicht modifizieren kannst, um zu sehen, welche Eingriffe welche Folgen haben.
Programmieren bedeutet auch, Fehler machen zu dürfen, aus denen gelernt werden kann.

Daher kann ich Dir nur dringend ans Herz legen, eine fehlerfreie vbcc-Umgebung zu schaffen, ansonsten wirst Du Dich schwer tun, die Beispielprogramme nachzuvollziehen.

Falls Du nicht weißt woher Du das NDK3.9 bekommen sollst, hier ein Link:
http://aweb.sunsite.dk/files/dev/NDK3.9.lzx


Zitat:
Ausser ggf. Optionen und Kommentaren ist es doch alles das selbe.

Eben...


Zitat:
Ich hab ASM-Pro.

Den kenne ich nicht.

Nichtsdestotrotz:

code:
*************************************
**
** Copyright © 1997 J.v.d.Loo
*
** Example stuff for the use of one of the two CIA-B timers - in mind
** the RKM Hardware Reference Manual from 1989, Appendix F, page 317 - 333.
*
** By the way: This code is reentrant.
*


	IFD	__G2
		MACHINE	MC68000
		OPT	OW-
		OUTPUT	RAM:AudioGrabber
	ENDC

	include	exec/exec_lib.i
	include	exec/interrupts.i
	include	exec/execbase.i

	include	dos/dos.i
	include	dos/dosextens.i

	include	resources/cia_lib.i

	include	hardware/custom.i
	include	hardware/cia.i


   STRUCTURE	__Table,0
	APTR	_SysBase
	APTR	_CIABBase
	APTR	_ThisTask

	APTR	_Memory
	ULONG	_AudioHit

	ULONG	_Timer				CIA-B Timer A (#0) or B (#1)
	ULONG	_PAL_NTSC			PAL (#0) or NTSC (#2) machine
	ULONG	_Counter

	STRUCT	_IRQStruct,IS_SIZE		CIA-B interrupt structure
	STRUCT	_IRQCause,IS_SIZE		By Cause() required one

	ALIGNLONG
	LABEL	tb_SIZEOF


AUDSIZE	EQU	398				As larger this buffer is as less noises occur (short blips)

_custom	equ	$DFF000
_ciaa	equ	$BFE001
_ciab	equ	$BFD000


_main
	lea	-tb_SIZEOF(sp),sp		Make some room on stack (for data area)
	movea.l	sp,A4				Store address data area into processor register A4

	movea.l	4.w,A6				Get Exec base
	move.l	A6,_SysBase(A4)

	suba.l	A1,A1
	jsr	_LVOFindTask(A6)
	move.l	D0,_ThisTask(A4)		Store address of own process structure

	move.l	#AUDSIZE,D0
	moveq	#MEMF_CHIP,D1
	jsr	_LVOAllocMem(A6)
	move.l	D0,_Memory(A4)
	bne.s	.goon

	moveq	#103,D0
	bra.w	_exit
.goon

	cmpi.b	#50,PowerSupplyFrequency(A6)	Which power frequency?
	bne.s	.NTSC

	clr.l	_PAL_NTSC(A4)			It's PAL
	bra.s	_OpenResource

.NTSC
	move.l	#2,_PAL_NTSC(A4)		It's NTSC


_OpenResource
	lea	_CIABName(pc),A1
	moveq	#0,D0				Any version
	jsr	_LVOOpenResource(A6)		Open it...
	move.l	D0,_CIABBase(A4)
	beq.w	_ErrorOpenResource

*
** Set up CIA-B interrupt structure
*
	lea	_IRQStruct(A4),A0		Initialize IRQ structure I

	clr.l	LN_PRED(A0)			Not really required because the resource
	clr.l	LN_SUCC(A0)			will overwrite it with its own settings

	move.b	#NT_INTERRUPT,LN_TYPE(A0)
	move.b	#32,LN_PRI(A0)

	move.l	A4,IS_DATA(A0)			Pointer to data array

	lea	_IRQCode(pc),A1
	move.l	A1,IS_CODE(A0)			Code to execute

	lea	_IRQName(pc),A1
	move.l	A1,LN_NAME(A0)

*
** Set up Cause() interrupt structure
*
	lea	_IRQCause(A4),A0		Initialize IRQ structure II

	clr.l	LN_PRED(A0)			Not really required - these two are
	clr.l	LN_SUCC(A0)			dynamically changed by Exec

	move.b	#NT_UNKNOWN,LN_TYPE(A0)		Type should be un-known - type assigned by Exec!
	move.b	#32,LN_PRI(A0)			Only -32, -16, 0, +16 and +32 supported!

	move.l	A4,IS_DATA(A0)			Pointer to data array

	lea	_IRQCauseCode(pc),A1
	move.l	A1,IS_CODE(A0)			Code to execute

	lea	_IRQName(pc),A1
	move.l	A1,LN_NAME(A0)

*
** Since the data area is located on the stack and we don't know which values
** the data contain since they are uninitialized, and a call to AddICRVector
** causes immediately the interrupt code to be started and we also avoid a
** Disable() call, we have to initialize _Counter and _AudioHit here:
*
	clr.l	_Counter(A4)
	clr.l	_AudioHit(A4)

*
** Attempt to start one of the CIA-B timer interrupts...
*
	moveq	#0,D2				Start with Timer A
	movea.l	_CIABBase(A4),A6
.try
	lea	_IRQStruct(A4),A1		IRQ to execute (immediately)
	move.l	D2,D0				Timer (which one?)
	jsr	_LVOAddICRVector(A6)
	tst.l	D0
	beq.s	_GotIRQ				If the interrupt is free

	addq.b	#1,D2				Next timer
	cmpi.b	#2,D2				Timer C?
	beq.w	_ErrorTimer			Timer C does not exist!
	bra.s	.try				Else try Timer B

_GotIRQ
	move.l	D2,_Timer(A4)			Save indicator for later (0 = Timer A, 1 = Timer B)

	lea	_ciab,A0			CIA-B hardware address ($BFD000)

	move.l	_PAL_NTSC(A4),D0		0 or 2
	lea	_PAL_NTSC_Times(pc),A1
	move.w	0(A1,D0.l),D0			PAL time or NTSC time value to delay between each occurring

	tst.l	_Timer(A4)			0 or 1
	bne.s	_TimerB				1 = Timer B

_TimerA
	move.b	ciacra(A0),D1			Control register Timer A
	andi.b	#%10000000,D1			Select mode (bit 7 currently unused)
	ori.b	#1,D1				Set Timer A to start
	move.b	D1,ciacra(A0)
	move.b	#%10000001,ciaicr(A0)		Enable Timer A

	move.b	D0,ciatalo(A0)			Write delay time into registers
	lsr.w	#8,D0
	move.b	D0,ciatahi(A0)
	bra.s	_ok				Done

_TimerB
	move.b	ciacrb(A0),D1			Control register Timer B
	andi.b	#%10000000,D1			Select mode
	ori.b	#1,D1				Set Timer B to start
	move.b	D1,ciacrb(A0)
	move.b	#%10000010,ciaicr(A0)		Enable Timer B

	move.b	D0,ciatblo(A0)			Write delay time into registers
	lsr.w	#8,D0
	move.b	D0,ciatbhi(A0)

_ok

*	btst	#CIAB_GAMEPORT0,_ciaa		Left mouse button (a busy loop - a absolute taboo)
*	bne.s	_wait				- so we use the following construct:

	move.l	#SIGBREAKF_CTRL_C,D0		Wait for a control-c signal, when pressed,
	movea.l	_SysBase(A4),A6			awake us,
	jsr	_LVOWait(A6)			otherwise sleep...

	lea	_custom,A0
	move.w	#15,dmacon(A0)			Turn off audio channels 0 - 4

	movea.l	_CIABBase(A4),A6
	lea	_IRQStruct(A4),A1
	move.l	_Timer(A4),D0
	jsr	_LVORemICRVector(A6)		Stop only the CIA-B interrupt - since the other one
*						is raised by software...

	moveq	#0,D0
_exit
	move.l	D0,D2

	tst.l	_Memory(A4)
	beq.s	1$

	movea.l	_SysBase(A4),A6
	movea.l	_Memory(A4),A1
	move.l	#AUDSIZE,D0
	jsr	_LVOFreeMem(A6)

1$
	move.l	_ThisTask(A4),A0
	move.l	D2,pr_Result2(A0)		Set error code to process
	lea	tb_SIZEOF(sp),sp		Restore stack
	move.l	D2,D0
	rts

_ErrorTimer
	moveq	#50,D0
	bra.s	_exit

_ErrorOpenResource
	moveq	#100,D0
	bra.s	_exit

*
** D0/D1/A0/A1/A5/A6 scratch registers
*
_IRQCode	; Through CIA-B hardware caused interrupt...
	movea.l	A1,A5				IS_DATA

	move.l	_Counter(A5),D0			Current position for data to be stored
	cmpi.l	#AUDSIZE,D0			End of buffer reached?
	bne.s	.getaud				If so...

	moveq	#0,D0

.getaud
	movea.l	_Memory(A5),A0
	lea	_ciaa,A1			CIA-A hardware address

	move.b	ciaprb(A1),D1			Get byte at parallel device
	addi.b	#128,D1				Make a signed byte
	move.b	D1,0(A0,D0.l)			Store the byte

	addq.l	#1,D0				One more stored
	move.l	D0,_Counter(A5)			and remember amount

	cmpi.l	#4,D0				Indicator for start audio (leading bytes)
	bne.s	.out				If not...

	tst.l	_AudioHit(A5)			If already fired up audio...
	bne.s	.out

	lea	_IRQCause(A5),A1
	movea.l	_SysBase(A5),A6
	jsr	_LVOCause(A6)			Start audio hardware once

.out
	lea	$DFF000,A0			In case of a VBlank IRQ...
	moveq	#0,D0				Set Z-Flag
	rts

*
** D0/D1/A0/A1/A5 scratch registers - A6 must remain intact
*
_IRQCauseCode	; Executed each ~1/16688 second - regardless what for a machine and which power supply!
	movea.l	A1,A5				IS_DATA

	tst.l	_AudioHit(A5)			Already fired up audio channel 0?
	bne.s	.out				If so...

	bsr.s	.startaudio

.out
	moveq	#0,D0				Ready and out
	rts


.startaudio
	movea.l	_Memory(A5),A0			Address CHIP memory
	lea	_custom,A1			DMA hardware address

	move.l	A0,aud0+ac_ptr(A1)		Address memory
	move.w	#AUDSIZE/2,aud0+ac_len(A1)	Amount in words of data
	move.w	#214,aud0+ac_per(A1)		Period 214 ~= 16688 Hz, "C-3"
	move.w	#64,aud0+ac_vol(A1)		Full volume

	move.l	A0,aud1+ac_ptr(A1)		Address memory
	move.w	#AUDSIZE/2,aud1+ac_len(A1)	Amount in words of data
	move.w	#214,aud1+ac_per(A1)		Period 214 ~= 16688 Hz, "C-3"
	move.w	#64,aud1+ac_vol(A1)		Full volume

	move.l	A0,aud2+ac_ptr(A1)		Address memory
	move.w	#AUDSIZE/2,aud2+ac_len(A1)	Amount in words of data
	move.w	#214,aud2+ac_per(A1)		Period 214 ~= 16688 Hz, "C-3"
	move.w	#64,aud2+ac_vol(A1)		Full volume

	move.l	A0,aud3+ac_ptr(A1)		Address memory
	move.w	#AUDSIZE/2,aud3+ac_len(A1)	Amount in words of data
	move.w	#214,aud3+ac_per(A1)		Period 214 ~= 16688 Hz, "C-3"
	move.w	#64,aud3+ac_vol(A1)		Full volume

	move.w	#$800F,dmacon(A1)		Turn on audio channels 0 - 4

	st.b	D0
	move.l	D0,_AudioHit(A5)		Indicate audio started
	rts


_PAL_NTSC_Times
	dc.w	42,43		709379/16688 = ~42, 715909/16688 = ~43	(1/16688 second till next
*				IRQ will be raised)
_IRQName
	dc.b	'AudioGrabber',0
_CIABName
	dc.b	'ciab.resource',0

	END


Bitte beschwere Dich nicht, dass Kommentare ohne Semikolon eingeleitet werden. Ist ein Charakteristikum von Devpac.


Zitat:
OS4 kommt bald für Classic, dann gibts CIA's.

Vorsicht, Hardwarebausteine vorhanden heißt nicht, dass diese über die originalen Betriebssystemfunktionen angesprochen werden können. Falls OS4 keine CIA-Resource offeriert, stehst Du im Regen.
Es wäre auch möglich, dass OS4 die Abwärts-Timer für sich selber benötigt - dann kannst Du zwar die CIA-Resource öffnen, bekommst aber trotzdem keinen Zugriff auf die Timer.


Gruß

[ - Antworten - Zitieren - Direktlink - ]

27.10.2007, 19:33 Uhr

jolo
Posts: 110
Nutzer
@whose:

Tach auch. :)

Zitat:
Ich denke nicht, daß SoftInterrupts so häufig Verwendung finden, es sei denn, man möchte dafür Sorge tragen, daß einen das Multitasking nicht bei zeitkritischen Aufgaben ausbremst. Hardware-Interrupts sind meiner Meinung nach eigentlich häufiger anzutreffen (u.A. für die CIA-Timer).

Hmmm, da habe ich aber Mist gebaut wenn's keiner versteht...
Software-Interrupts werden natürlich auch von einem Hardwarebaustein generiert, allerdings liegt es am Betriebssystem die Hardwarekomponenten auszuwählen, die für diesen Vorgang nötig sind.

Unter OS3.x wird meistens der CIA-A Baustein dafür verwandt (unter 1.x war's der CIA-B) - welcher eh schon vom Timer-Device verwendet wird - somit sagt eine Level-2 (CIA-A) Interrupt-Quelle dem Timer-Device, dass eine vorgegebene Zeit verstrichen ist, was wiederum dazu führt, dass das Timer-Device mittels einem Cause()-Aufruf, einen Level-1 Interrupt veranlasst.
Dieser Interrupt ( Cause() ) wird allerdings erst dann ausgeführt, wenn alle anderen Interrupts schon abgearbeitet wurden, d.h. die hardwarenahen Interrupts der Ebene 2, da alle anderen Ebenen ( 6 - 3 ) sowieso schon abgearbeitet wurden.

Software-Interrupts heißen so, weil sie von einer Software angefordert (BeginIO) und durch eine Software veranlasst (Cause) werden. Nichtsdestotrotz wird ein Prozessor-Interrupt (Level-1) durch einen Hardwarebaustein ausgelöst.
Die minimale Verzögerung beim Ausführen der Software ist dabei zu verschmerzen, es sei denn, jemand braucht die absolut exakte Taktung - was aber selbst bei der Verwendung des CIA-B (Level-6) Hardwarebausteins nicht immer gelingt - bedingt durch Rundungsfehler.


Zitat:
Wenn ich ehrlich bin muß ich sagen, daß ich dieses Beispiel für Anfänger wesentlich schwerer zu durchschauen finde, weil dort nicht so wirklich klar wird, auf welche Weise der SoftInt "aufgerufen" wird bzw. was diesen Interrupt eigentlich auslöst.

Da gebe ich Dir vollkommen Recht. Das Problem bei der Amiga-Programmierung ist, dass wir immer verstehen wollen, wie was funktioniert, weil wir auch immer die Strukturen von Hand initialisieren müssen. Unter anderen Betriebssystemen benutzt man Funktionen, die keinen Einblick auf die Zugrunde liegenden Eigenschaften gewähren, also mehr oder weniger Black-Boxes nacheifern.
Das Amiga-Betriebssystem ist eigentlich sehr ausgeklügelt angelegt, was die Effizienz der Hardware angeht - es gibt natürlich auch hier Ecken und Kanten - allerdings mangelt es an Dokumentationen, wie man was verwenden soll.
Wir benutzen z.B. das Timer-Device um eine gewisse Zeitspanne zu warten, ohne zu Wissen, dass es den CIA-A Baustein in Beschlag nimmt.
Wir benutzen das Timer-Device um Interrupts zu generieren, ohne zu Wissen, dass es entweder den CIA-A/CIA-B Baustein oder auch die Video-Hardware benutzt, um in bestimmten Zeitabständen eine bestimmte Aktion zu tätigen.

Alles, was mit Timing zu tun hat, setzt voraus, dass irgendwo die Hardware vorhanden ist, die in der Lage ist, in bestimmten Zeitabständen oder auch nur einmalig, dem Prozessor zu signalisieren, das ein bestimmter Zustand erreicht worden ist.


Zurück zu unserem Beispiel.
Wie oben eingeräumt, gebe ich Dir vollkommen Recht; das Beispiel ist wirklich nicht ganz einfach zu verstehen.

Es wird ein "echter" Interrupt generiert - allerdings nur einmal aufgerufen (StartIRQ).
Da wir einen wiederkehrenden Interrupt benötigen, jedoch unsere Interrupt-Routine nur einmal aufgerufen wird, greifen wir zu einem Trick. Wir starten innerhalb unseres Interrupts unseren Interrupt erneut - dabei sagen wir dem Timer-Device, dass es erst nach soundsovielen Sekunden uns erneut aufrufen soll.
Nachdem dies geschehen ist und unser Interrupt beendet wurde, vergehen soundsoviele Sekunden bis unser Interrupt erneut aufgerufen wird - und wir wiederholen das Prozedere.

Nehme an, Du müsstest in einer Schleife jedes Mal 400 Millisekunden warten um irgendeinen Wert, der sich alle 400 Millisekunden ändert, zu lesen. Im Prinzip rufst Du dann auch immer DoIO() auf - vorher hast Du die "Auszeit" mit 400 Millisekunden festgelegt.
Im Prinzip verwenden wird das Gleiche für unseren Interrupt, sagen aber dem Timer-Device, dass es keinen Prozess/Task etwas signalisieren soll (mp_SigTask), sondern einem Interrupt (PA_SOFTINT) und verwenden nicht DoIO sondern BeginIO.


Zitat:
Es wird auch nicht ganz klar, welchen Nutzen man daraus ziehen kann, daß "andere Interrupts" davon aufgerufen werden können.

Das geht noch tiefer in die Materie als geplant...
Sei's drum.
Bestimmte Gerätetreiber und Hardwarekomponenten darf man nur von niederwertigen Interrupts aus aufrufen - da diese selber Interrupts oder DMAs verwenden. Um einen Systemabsturz zu vermeiden, ist man dann normalerweise gezwungen, Cause() von innerhalb eines höherwertigen Interrupts aus aufzurufen, was wiederum mehr Sorgfalt und Arbeit bei der Programmierung bedeutet.

Als Beispiel:
Versuche mal innerhalb eines CIA-B Interrupts (erstes Beispiel) das Audio-Device zu benutzen - und poste hier mal die GURUs, falls Du das Glück hast, das noch welche ausgegeben werden können... ;)


Zitat:
Für die Vorstellung Vieler dürfte ein Interrupt etwas "im Wesen deutlich verschiedenes" zu Tasks/Prozessen sein, diese Beispiel zeigt aber im Grunde (korrigiere mich, wenn ich da falsch liege) einen Task, der teilweise aus dem "normalen" Multitasking rausgenommen und auf Interrupt-Ebene verlagert wurde.

Korrigiert?
Korrigiert! :)

Wie oben beschrieben, ist es ein echter Interrupt - nur halt niederwertiger.


Zitat:
Ich denke nicht, daß ein Unerfahrerener so ohne weiteres kapiert, daß dieser "Interrupt" quasi wie ein normaler Task via Signal vom timer.device "aufgeweckt" wird (was ja keineswegs der landläufigen Vorstellung von einem Interrupt entspricht).

Wie gesagt, deshalb habe ich ja auch versucht zu erklären, warum ich so gerne das Ganze abstrahiert hätte. Ist mir ganz klar nicht gelungen. :(
Ich hätte zudem darauf hinweisen müssen, dass nicht das Timer-Device den Interrupt auslöst sondern nur anfordert.
Auslöser ist Cause() - und das verursacht den Level-1 Interrupt - durch das Setzen des 2. Bits im INTREQ Register ($DFF09C). Damit ist wiederum der Hardwarebaustein gefunden, der erst einen Level-1 Interrupt unter Classics ermöglicht, Agnus.


Zitat:
[...] (er hackt ganz gern ;) ).

Dann könnte er sich auch simultan in der Linux-Gemeinde betätigen; dort scheint hacken eine Art Sport zu werden.
Vielleicht schafft's Hacken auch als Olympische Disziplin anerkannt zu werden... wer weiß. ;)


Zitat:
Ich fand das erste Beispiel jedenfalls schön praxisbezogen und gut dokumentiert, Abstraktion wie im letzten Beispiel ist eigentlich ein Thema für fortgeschrittenere Programmierer und für seine Zwecke auch gar nicht wirklich nötig.

Richtig, es war auch nicht für ihn gedacht - sondern für die, die systemkonform programmieren müssen und/oder die sich nicht mit Details auseinander setzen wollen, da man die Routinen so wie sie sind, verwenden kann ( InitIRQ(), StartIRQ(), DeleteIRQ() ).
Alles andere, also die Implementierung selber, soll der Nutzer ja gar nicht sehen und auch darüber keinen Kopf machen.
Wie gesagt, den Datentyp wollte ich verstecken - das geht aber nicht bei nur einem Quellcode...
Stelle Dir vor, die drei Funktionen wären in einer Link-Lib. Der Datentyp den InitIRQ() zurückgibt würde in einer Include-Datei mit "VOID *" spezifiziert anstatt mit "struct TimeOutIRQData *" - und nur die drei Funktionen wüssten, das "VOID *" in Wirklichkeit "struct TimeOutIRQData *" wäre. Wäre es dann nicht extrem leicht für einen Nutzer, diese Funktionen zu verwenden?


Ciao - ich bin raus :)

[ - Antworten - Zitieren - Direktlink - ]

27.10.2007, 20:01 Uhr

whose
Posts: 2156
Nutzer
Grüezi! :)

Zitat:
Original von jolo:

Hmmm, da habe ich aber Mist gebaut wenn's keiner versteht...
Software-Interrupts werden natürlich auch von einem Hardwarebaustein generiert, allerdings liegt es am Betriebssystem die Hardwarekomponenten auszuwählen, die für diesen Vorgang nötig sind.


Mir war das soweit schon klar, es ist nur schwer zu vermitteln über den Beispiel-Code ;) Der Beginner sieht nur, daß da ein Signal vom timer.device gesendet wird und denkt im ersten Moment "Ah, wie ich das vorher auch schon gemacht habe. Wozu dann aber den SoftInt? Welchen Gewinn habe ich dadurch? Ist ein SoftInt wirklich was ganz anderes als ein normaler Task? Wieso nicht direkt die CIA-Timer und deren Interrupt nutzen?"

Es ist ja nicht so, daß man jedes winzige Detail dieses Ablaufs erklären müßte, aber ein grober Überblick über die tatsächlichen Abläufe kann nie schaden und hilft u.U. auch beim Verständnis von fortgeschritteneren Techniken.

Zitat:
Zitat:
Wenn ich ehrlich bin muß ich sagen, daß ich dieses Beispiel für Anfänger wesentlich schwerer zu durchschauen finde, weil dort nicht so wirklich klar wird, auf welche Weise der SoftInt "aufgerufen" wird bzw. was diesen Interrupt eigentlich auslöst.

Da gebe ich Dir vollkommen Recht. Das Problem bei der Amiga-Programmierung ist, dass wir immer verstehen wollen, wie was funktioniert, weil wir auch immer die Strukturen von Hand initialisieren müssen. Unter anderen Betriebssystemen benutzt man Funktionen, die keinen Einblick auf die Zugrunde liegenden Eigenschaften gewähren, also mehr oder weniger Black-Boxes nacheifern.


Das ist leider so unter anderen OSsen und führt auch oft zu einem völligen Mißverständnis der zugrunde liegenden Prinzipien. Ich denke, grundlegendes Verständnis der Technik kann nie schaden, Vorteile von Blackboxes hin oder her. Ich habe festgestellt, daß Blackbox-Prinzipien immer erst dann verstanden werden, wenn man tiefer in die Materie eingestiegen ist und die Probleme selbst kennengelernt hat, die bei "Hardware-Banging" auftreten. Learning by doing ;)

Ging mir selbst übrigens auch oft so ;)

Zitat:
Das Amiga-Betriebssystem ist eigentlich sehr ausgeklügelt angelegt, was die Effizienz der Hardware angeht - es gibt natürlich auch hier Ecken und Kanten - allerdings mangelt es an Dokumentationen, wie man was verwenden soll.

Dein Wort in der Friedens Ohren...

Zitat:
Wir benutzen z.B. das Timer-Device um eine gewisse Zeitspanne zu warten, ohne zu Wissen, dass es den CIA-A Baustein in Beschlag nimmt.
Wir benutzen das Timer-Device um Interrupts zu generieren, ohne zu Wissen, dass es entweder den CIA-A/CIA-B Baustein oder auch die Video-Hardware benutzt, um in bestimmten Zeitabständen eine bestimmte Aktion zu tätigen.


Ich sags mal so: Das sind Irrtümer, die hauptsächlich den Blackbox-Spezialisten unterlaufen. Aus eben dem Grund, weil sie die Technik dahinter gar nicht kennen, nicht wissen, was innerhalb der Blackbox im Groben abläuft, auf welchen Bausteinen/technischen Merkmalen die Blackbox basiert usw.


Zitat:
Zurück zu unserem Beispiel.
Wie oben eingeräumt, gebe ich Dir vollkommen Recht; das Beispiel ist wirklich nicht ganz einfach zu verstehen.

Es wird ein "echter" Interrupt generiert - allerdings nur einmal aufgerufen (StartIRQ).
Da wir einen wiederkehrenden Interrupt benötigen, jedoch unsere Interrupt-Routine nur einmal aufgerufen wird, greifen wir zu einem Trick. Wir starten innerhalb unseres Interrupts unseren Interrupt erneut - dabei sagen wir dem Timer-Device, dass es erst nach soundsovielen Sekunden uns erneut aufrufen soll.
Nachdem dies geschehen ist und unser Interrupt beendet wurde, vergehen soundsoviele Sekunden bis unser Interrupt erneut aufgerufen wird - und wir wiederholen das Prozedere.


Ja, der Teil ist eigentlich leicht verständlich ;) Man fragt sich als Anfänger aber automatisch, was die Quelle des Aufrufs ist, sieht nur das Signaling, kümmert sich nicht weiter um die "Kleinigkeit" PA_SOFTINT und spart sich darüber dann auch die Mühe, das nicht ganz einfach verständliche Kapitel "Exec: Interrupts" in den RKMs gründlich zu lesen, weil das ja dem normalen Task-Signaling sehr ähnlich ist. Daß das timer.device in diesem Fall mit Cause() statt Signal() arbeitet, bekommt man gar nicht mit ;)

Zitat:
Nehme an, Du müsstest in einer Schleife jedes Mal 400 Millisekunden warten um irgendeinen Wert, der sich alle 400 Millisekunden ändert, zu lesen. Im Prinzip rufst Du dann auch immer DoIO() auf - vorher hast Du die "Auszeit" mit 400 Millisekunden festgelegt.
Im Prinzip verwenden wird das Gleiche für unseren Interrupt, sagen aber dem Timer-Device, dass es keinen Prozess/Task etwas signalisieren soll (mp_SigTask), sondern einem Interrupt (PA_SOFTINT) und verwenden nicht DoIO sondern BeginIO.


Und hier wird es dann endlich klar, daß der SoftInt wirklich ein Int ist, der via Cause() gestartet wird ;)

Zitat:
Zitat:
Ich denke nicht, daß ein Unerfahrerener so ohne weiteres kapiert, daß dieser "Interrupt" quasi wie ein normaler Task via Signal vom timer.device "aufgeweckt" wird (was ja keineswegs der landläufigen Vorstellung von einem Interrupt entspricht).

Wie gesagt, deshalb habe ich ja auch versucht zu erklären, warum ich so gerne das Ganze abstrahiert hätte. Ist mir ganz klar nicht gelungen. :(


Meiner Meinung nach für das Verständnis der ganzen Sache aber weit dienlicher in dieser Form. Abstrahiert wäre das Ganze für einen Anfänger auch abstrakt (d.h. ohne jeden Ankerpunkt für das Verständnis) geblieben.

Zitat:
Ich hätte zudem darauf hinweisen müssen, dass nicht das Timer-Device den Interrupt auslöst sondern nur anfordert.
Auslöser ist Cause() - und das verursacht den Level-1 Interrupt - durch das Setzen des 2. Bits im INTREQ Register ($DFF09C). Damit ist wiederum der Hardwarebaustein gefunden, der erst einen Level-1 Interrupt unter Classics ermöglicht, Agnus.


Ah, und wieder ein Anhaltspunkt mehr für ausgiebige Lektüre der RKMs mit ein klein wenig Hintergrundwissen ;) Jetzt verstehe sogar ich wieder ein klein wenig mehr, was mir vorher nie so ganz klar geworden ist. Danke schön! :)

Zitat:
Ich fand das erste Beispiel jedenfalls schön praxisbezogen und gut dokumentiert, Abstraktion wie im letzten Beispiel ist eigentlich ein Thema für fortgeschrittenere Programmierer und für seine Zwecke auch gar nicht wirklich nötig.

Zitat:
Wie gesagt, den Datentyp wollte ich verstecken - das geht aber nicht bei nur einem Quellcode...
Stelle Dir vor, die drei Funktionen wären in einer Link-Lib. Der Datentyp den InitIRQ() zurückgibt würde in einer Include-Datei mit "VOID *" spezifiziert anstatt mit "struct TimeOutIRQData *" - und nur die drei Funktionen wüssten, das "VOID *" in Wirklichkeit "struct TimeOutIRQData *" wäre. Wäre es dann nicht extrem leicht für einen Nutzer, diese Funktionen zu verwenden?


Wäre es ohne VOID * auch, zumindest für AmigaOS-Gewöhnte ;)

Aber hast schon Recht, bei konkreten Implementierungen muß man nicht unbedingt wissen, wie sie intern funktionieren. Deine Erläuterungen hier haben aber definitiv dazu beigetragen, die Abläufe im OS und die Stolperfallen, die auf einen lauern können, besser zu verstehen, da bin ich absolut sicher. Nochmals vielen Dank dafür!

Grüße

--
---

:boing: µA1 PPC 750GX-800
:boing: A4000 PPC 604e-233

[ - Antworten - Zitieren - Direktlink - ]

29.10.2007, 09:36 Uhr

MaikG
Posts: 5172
Nutzer
>Den kenne ich nicht.

>Nichtsdestotrotz:


AsmPro scheint da die OS3.9 Includes nicht zu aktzeptieren.
Keine ahnung warum. In den Programmen die ich sonst damit
Assemblert habe waren die offsets direkt drin.

[ - Antworten - Zitieren - Direktlink - ]

29.10.2007, 11:29 Uhr

Holger
Posts: 8116
Nutzer
Zitat:
Original von jolo:
Stelle Dir vor, die drei Funktionen wären in einer Link-Lib. Der Datentyp den InitIRQ() zurückgibt würde in einer Include-Datei mit "VOID *" spezifiziert anstatt mit "struct TimeOutIRQData *" - und nur die drei Funktionen wüssten, das "VOID *" in Wirklichkeit "struct TimeOutIRQData *" wäre. Wäre es dann nicht extrem leicht für einen Nutzer, diese Funktionen zu verwenden?

...und vor allem munter mit anderen, völlig falschen Zeigern zu mischen. "void*", bzw. das zugehörige #define'd "APTR" wird ja gerade im AmigaOS sehr gerne eingesetzt, mit den entsprechenden Nachteilen.

Es gibt gar keinen Grund, "void*" einzusetzen, wenn man eine Struktur kapseln will. Man kann einfach "struct Foo;" deklarieren, und "struct Foo*" für seinen Funktionen verwenden--gekapselt, da Inhalt für den Aufrufer nicht bekannt, trotzdem typsicher, da struct Foo*" trotzdem etwas anderes als "struct Bar*" wäre.

Und vor allem entsteht kein Konflikt, wenn eine Implementierung, die die vollständige Beschreibung der Struktur kennt, sowohl die öffentliche Schnittstelle, als auch die interne Beschreibung via #include einbindet. Der Compiler liefert damit eine Kompatibilitätsprüfung frei Haus.

Wozu also void* ?

mfg

--
Good coders do not comment. What was hard to write should be hard to read too.

[ - Antworten - Zitieren - Direktlink - ]

30.10.2007, 23:46 Uhr

jolo
Posts: 110
Nutzer
@MaikG:

Habe die die fehlenden Assembler Include-Dateien auf http://www.amimedic.de hochgeladen, die ich benutze (OS3.0).

Gruß

[ - Antworten - Zitieren - Direktlink - ]

30.10.2007, 23:48 Uhr

jolo
Posts: 110
Nutzer
@Holger:

Hi.

Zitat:
...und vor allem munter mit anderen, völlig falschen Zeigern zu mischen. "void*", bzw. das zugehörige #define'd "APTR" wird ja gerade im AmigaOS sehr gerne eingesetzt, mit den entsprechenden Nachteilen.

Da gebe ich Dir vollkommen Recht. Allerdings liegt das begründet in den verwendeten Programmiersprachen fürs Betriebssystem: m68k-Assembler und C. Und diese dürfen per Definition mit "void *" so ziemlich alles machen.
Bei der Benutzung von C++ fliegen einem dann die APTRs als Fehlermeldung nur so um die Ohren.


Zitat:
Es gibt gar keinen Grund, "void*" einzusetzen, wenn man eine Struktur kapseln will. Man kann einfach "struct Foo;" deklarieren, und "struct Foo*" für seinen Funktionen verwenden--gekapselt, da Inhalt für den Aufrufer nicht bekannt, trotzdem typsicher, da struct Foo*" trotzdem etwas anderes als "struct Bar*" wäre.

Ja, auch das ist richtig.
Allerdings bringt mir das recht wenig, wenn ich den Quellcode offen lege; mehr noch, das hätte den durchschnittlichen C-Programmierer nur durcheinander gebracht.
Dementsprechend denke ich, dass ich bei klarer Typen-Trennung (Object * ist nicht gleichzusetzen mit TimeOutIRQData * und void * nicht mit TimeOutIRQData *) einen besser lesbaren Ansatz geschaffen habe um zu verdeutlichen, was ich unter Abstraktion verstehe, jedenfalls für diejenigen, die sich nicht semi- oder professionell mit der C-Programmierung beschäftigen.

Gruß

[ - Antworten - Zitieren - Direktlink - ]

31.10.2007, 08:31 Uhr

Solar
Posts: 3680
Nutzer
Zitat:
Original von jolo:
...mehr noch, das hätte den durchschnittlichen C-Programmierer nur durcheinander gebracht.


Der "durchschnittliche" C-Programmierer geht jedesmal mit einer "Black Box"-Struct um, wenn er eine Datei öffnet (FILE *).

[ - Antworten - Zitieren - Direktlink - ]

31.10.2007, 22:24 Uhr

MaikG
Posts: 5172
Nutzer
>Habe die die fehlenden Assembler Include-Dateien auf http://www.amimedic.de hochgeladen, die ich benutze (OS3.0).

Danke aber da fehlen Dateien.

exec/interrupts.i

meldet der Compiler als fehlend und diese ist auch nicht da.

[ - Antworten - Zitieren - Direktlink - ]

01.11.2007, 12:24 Uhr

jolo
Posts: 110
Nutzer
@Solar:

Hi.

Zitat:
...mehr noch, das hätte den durchschnittlichen C-Programmierer nur durcheinander gebracht.

Zitat:
Der "durchschnittliche" C-Programmierer geht jedesmal mit einer "Black Box"-Struct um, wenn er eine Datei öffnet (FILE *).


Es geht nicht um das Abstrahieren selber und auch nicht um die Implementierung. Jeder kann das machen wie es ihm beliebt.
Was ich mit einfachen Worten versucht habe aufzuzeigen, und was mir wieder nicht gelungen ist, dass unter Verwendung eines anderen Datentyps, der Nutzer dieser Funktionen, keinen Einblick in die Materie bekommt und auch nicht benötigt, um diese Funktionen zu verwenden.
Da der Quellcode offen liegt und nur aus einer Datei besteht, ist die Einführung eines anderen Datentyps zwar möglich, würde aber den durchschnittlichen C-Programmierer (und als solchen bezeichne ich mich selber) durcheinander bringen, weil dann in den entsprechenden Routinen gecastet werden muss - und das ist bestimmt dem Lesen nicht förderlich.

Es steht Dir und anderen frei, das Beispiel so zu modifizieren, dass Diplom-Informatiker daran ihre Freude finden - und ich wette, dass ich dann Schwierigkeiten hätte, meinen, von euch verwendeten Code, zu verstehen. :)


Gruß

[ - Antworten - Zitieren - Direktlink - ]

01.11.2007, 12:28 Uhr

jolo
Posts: 110
Nutzer
@MaikG:

Hi.

Bei den von mir bereitgestellten Dateien handelt es sich ausschließlich um Funktionsoffsets für die entsprechenden Bibliotheken. Der Rest muss vom NDK3.9 bezogen werden.

1. Schritt
NDK3.9 saugen

2. Schritt
NDK3.9 entpacken und den Pfad merken, wo Du das NDK installiert hast.

3. Schritt
Verweis (Assign) innerhalb der User-Startup-Sequenz aufs NDK erstellen.
(assign SDK_OS3: "[path...]/NDK_3.9/Include/")

4. Schritt
Rechner neu starten.


--- Assembler ---

5. Schritt
Assembler Include-Pfad anpassen - muss auf "SDK_OS3:include_i/" verweisen.
Da ich ASM-Pro nicht kenne, kann ich auch nicht sagen, wie Du das bewerkstelligen musst. Also die Anleitung lesen.

6. Schritt
Die von mir bereitgestellten Dateien in die entsprechenden Verzeichnisse des "SDK_OS3:include_i" kopieren.


--- C ---

7. Schritt
Alle Verweise des vbcc Compilers aus der User-Startup-Sequenz ausmaskieren (; - Semikolon) oder permanent entfernen (löschen). Evt. solltest Du Dich dafür entscheiden, die komplette(n) vbcc Installation(en) zu entfernen (delete vbcc: all force).

8. Schritt
vbcc Dateien downloaden (http://sun.hasenbraten.de/vbcc/).
(vbcc_bin_amigaos68k.lha)
(vbcc_target_m68k-amigaos.lha)

9. Schritt
Rechner neu starten.

10. Schritt
vbcc Dateien entpacken.

11. Schritt
Installationsroutinen aufrufen - wenn Du nach dem NDK/SDK gefragt wirst, "SDK_OS3:include_h" angeben. Das war's.

12. Schritt
Evt. die von vbcc (Frank Wille) bereitgestellte "amiga.lib" ("vbcc:targets/m68k-amigaos/lib/amiga.lib") durch die originale Commodore "amiga.lib" ("SDK_OS3:linker_libs/amiga.lib") ersetzen.

13. Schritt
Die beiden von mir bereitgestellten Dateien für die Benutzung der CIAs in die entsprechenden Verzeichnisse kopieren.


Gruß

[ - Antworten - Zitieren - Direktlink - ]

01.11.2007, 13:55 Uhr

Solar
Posts: 3680
Nutzer
Zitat:
Original von jolo:
@Solar:

Es steht Dir und anderen frei, das Beispiel so zu modifizieren, dass Diplom-Informatiker daran ihre Freude finden...


Ich werde einen Teufel tun, weil ich kein solcher bin. Aber ich habe jeden Tag mit Code zu tun, bei dem sich der Autor die eine oder andere Abkürzung geleistet hat und später nicht mehr dazu kam, das auszubessern. Bei zwei Bildschirmseiten ist das unschön, bei zwei Übersetzungseinheiten lästig - aber bei 2 MByte (gepacktem) Source Code ist es die Pest. ;)

[ - Antworten - Zitieren - Direktlink - ]

01.11.2007, 21:42 Uhr

MaikG
Posts: 5172
Nutzer
>--- Assembler ---

Ich hab die NDK3.9 Dateien jetzt dazu kopiert bekomme aber einen fehler bei
AUDSIZE EQU 398

[ - Antworten - Zitieren - Direktlink - ]

02.11.2007, 17:56 Uhr

jolo
Posts: 110
Nutzer
Zitat:
Original von MaikG:
>--- Assembler ---

Ich hab die NDK3.9 Dateien jetzt dazu kopiert bekomme aber einen fehler bei
AUDSIZE EQU 398


Hab's doch geahnt, dass Devpac inkompatibel zu Deinem Assembler ist! :)
Probier es mal mit Semikolons:

code:
*************************************
**
** Copyright © 1997 J.v.d.Loo
*
** Example stuff for the use of one of the two CIA-B timers - in mind
** the RKM Hardware Reference Manual from 1989, Appendix F, page 317 - 333.
*
** By the way: This code is reentrant.
*
* A68k AudioGrabber.asm -iSDK_OS3:include_i
* BLink AudioGrabber.o TO AudioGrabber
*

	IFD	__G2
		MACHINE	MC68000
		OPT	OW-
		OUTPUT	RAM:AudioGrabber
	ENDC

	include	exec/exec_lib.i
	include	exec/interrupts.i
	include	exec/execbase.i

	include	dos/dos.i
	include	dos/dosextens.i

	include	resources/cia_lib.i

	include	hardware/custom.i
	include	hardware/cia.i


   STRUCTURE	__Table,0
	APTR	_SysBase
	APTR	_CIABBase
	APTR	_ThisTask

	APTR	_Memory
	ULONG	_AudioHit

	ULONG	_Timer				; CIA-B Timer A (#0) or B (#1)
	ULONG	_PAL_NTSC			; PAL (#0) or NTSC (#2) machine
	ULONG	_Counter

	STRUCT	_IRQStruct,IS_SIZE		; CIA-B interrupt structure
	STRUCT	_IRQCause,IS_SIZE		; By Cause() required one

	ALIGNLONG
	LABEL	tb_SIZEOF


AUDSIZE	EQU	398				; As larger this buffer is as less noises occur (short blips)

_custom	EQU	$DFF000
_ciaa	EQU	$BFE001
_ciab	EQU	$BFD000

* BITDEFs macros don't work for A68k; A68k is too old...
	IFND	MEMF_CHIP
MEMF_CHIP	EQU	2
	ENDC

_main
	lea	-tb_SIZEOF(sp),sp		; Make some room on stack (for data area)
	movea.l	sp,A4				; Store address data area into processor register A4

	movea.l	(4).w,A6			; Get Exec base

	move.l	A6,_SysBase(A4)

	suba.l	A1,A1
	jsr	_LVOFindTask(A6)
	move.l	D0,_ThisTask(A4)		; Store address of own process structure

	move.l	#AUDSIZE,D0
	moveq	#MEMF_CHIP,D1
	jsr	_LVOAllocMem(A6)
	move.l	D0,_Memory(A4)
	bne.s	.goon

	moveq	#103,D0
	bra.w	_exit
.goon

	cmpi.b	#50,PowerSupplyFrequency(A6)	; Which power frequency?
	bne.s	.NTSC

	clr.l	_PAL_NTSC(A4)			; It's PAL
	bra.s	_OpenResource

.NTSC
	move.l	#2,_PAL_NTSC(A4)		; It's NTSC


_OpenResource
	lea	_CIABName(pc),A1
	moveq	#0,D0				; Any version
	jsr	_LVOOpenResource(A6)		; Open it...
	move.l	D0,_CIABBase(A4)
	beq.w	_ErrorOpenResource

*
** Set up CIA-B interrupt structure
*
	lea	_IRQStruct(A4),A0		; Initialize IRQ structure I

	clr.l	LN_PRED(A0)			; Not really required because the resource
	clr.l	LN_SUCC(A0)			; will overwrite it with its own settings

	move.b	#NT_INTERRUPT,LN_TYPE(A0)
	move.b	#32,LN_PRI(A0)

	move.l	A4,IS_DATA(A0)			; Pointer to data array

	lea	_IRQCode(pc),A1
	move.l	A1,IS_CODE(A0)			; Code to execute

	lea	_IRQName(pc),A1
	move.l	A1,LN_NAME(A0)

*
** Set up Cause() interrupt structure
*
	lea	_IRQCause(A4),A0		; Initialize IRQ structure II

	clr.l	LN_PRED(A0)			; Not really required - these two are
	clr.l	LN_SUCC(A0)			; dynamically changed by Exec

	move.b	#NT_UNKNOWN,LN_TYPE(A0)		; Type should be un-known - type assigned by Exec!
	move.b	#32,LN_PRI(A0)			; Only -32, -16, 0, +16 and +32 supported!

	move.l	A4,IS_DATA(A0)			; Pointer to data array

	lea	_IRQCauseCode(pc),A1
	move.l	A1,IS_CODE(A0)			; Code to execute

	lea	_IRQName(pc),A1
	move.l	A1,LN_NAME(A0)

*
** Since the data area is located on the stack and we don't know which values
** the data contain since they are uninitialized, and a call to AddICRVector
** causes immediately the interrupt code to be started and we also avoid a
** Disable() call, we have to initialize _Counter and _AudioHit here:
*
	clr.l	_Counter(A4)
	clr.l	_AudioHit(A4)

*
** Attempt to start one of the CIA-B timer interrupts...
*
	moveq	#0,D2				; Start with Timer A
	movea.l	_CIABBase(A4),A6
.try
	lea	_IRQStruct(A4),A1		; IRQ to execute (immediately)
	move.l	D2,D0				; Timer (which one?)
	jsr	_LVOAddICRVector(A6)
	tst.l	D0
	beq.s	_GotIRQ				; If the interrupt is free

	addq.b	#1,D2				; Next timer
	cmpi.b	#2,D2				; Timer C?
	beq.w	_ErrorTimer			; Timer C does not exist!
	bra.s	.try				; Else try Timer B

_GotIRQ
	move.l	D2,_Timer(A4)			; Save indicator for later (0 = Timer A, 1 = Timer B)

	lea	_ciab,A0			; CIA-B hardware address ($BFD000)

	move.l	_PAL_NTSC(A4),D0		; 0 or 2
	lea	_PAL_NTSC_Times(pc),A1
	move.w	0(A1,D0.l),D0			; PAL time or NTSC time value to delay between each occurring

	tst.l	_Timer(A4)			; 0 or 1
	bne.s	_TimerB				; 1 = Timer B

_TimerA
	move.b	ciacra(A0),D1			; Control register Timer A
	andi.b	#%10000000,D1			; Select mode (bit 7 currently unused)
	ori.b	#1,D1				; Set Timer A to start
	move.b	D1,ciacra(A0)
	move.b	#%10000001,ciaicr(A0)		; Enable Timer A

	move.b	D0,ciatalo(A0)			; Write delay time into registers
	lsr.w	#8,D0
	move.b	D0,ciatahi(A0)
	bra.s	_ok				; Done

_TimerB
	move.b	ciacrb(A0),D1			; Control register Timer B
	andi.b	#%10000000,D1			; Select mode
	ori.b	#1,D1				; Set Timer B to start
	move.b	D1,ciacrb(A0)
	move.b	#%10000010,ciaicr(A0)		; Enable Timer B

	move.b	D0,ciatblo(A0)			; Write delay time into registers
	lsr.w	#8,D0
	move.b	D0,ciatbhi(A0)

_ok

*	btst	#CIAB_GAMEPORT0,_ciaa		; Left mouse button (a busy loop - a absolute taboo)
*	bne.s	_wait				; - so we use the following construct:

	move.l	#SIGBREAKF_CTRL_C,D0		; Wait for a control-c signal, when pressed,
	movea.l	_SysBase(A4),A6			; awake us,
	jsr	_LVOWait(A6)			; otherwise sleep...

	lea	_custom,A0
	move.w	#15,dmacon(A0)			; Turn off audio channels 0 - 4

	movea.l	_CIABBase(A4),A6
	lea	_IRQStruct(A4),A1
	move.l	_Timer(A4),D0
	jsr	_LVORemICRVector(A6)		; Stop only the CIA-B interrupt - since the other one
*						; is raised by software...

	moveq	#0,D0
_exit
	move.l	D0,D2

	tst.l	_Memory(A4)
	beq.s	1$

	movea.l	_SysBase(A4),A6
	movea.l	_Memory(A4),A1
	move.l	#AUDSIZE,D0
	jsr	_LVOFreeMem(A6)

1$
	move.l	_ThisTask(A4),A0
	move.l	D2,pr_Result2(A0)		; Set error code to process
	lea	tb_SIZEOF(sp),sp		; Restore stack
	move.l	D2,D0
	rts

_ErrorTimer
	moveq	#50,D0
	bra.s	_exit

_ErrorOpenResource
	moveq	#100,D0
	bra.s	_exit

*
** D0/D1/A0/A1/A5/A6 scratch registers
*
_IRQCode	; Through CIA-B hardware caused interrupt...
	movea.l	A1,A5				; IS_DATA

	move.l	_Counter(A5),D0			; Current position for data to be stored
	cmpi.l	#AUDSIZE,D0			; End of buffer reached?
	bne.s	.getaud				; If so...

	moveq	#0,D0

.getaud
	movea.l	_Memory(A5),A0
	lea	_ciaa,A1			; CIA-A hardware address

	move.b	ciaprb(A1),D1			; Get byte at parallel device
	addi.b	#128,D1				; Make a signed byte
	move.b	D1,0(A0,D0.l)			; Store the byte

	addq.l	#1,D0				; One more stored
	move.l	D0,_Counter(A5)			; and remember amount

	cmpi.l	#4,D0				; Indicator for start audio (leading bytes)
	bne.s	.out				; If not...

	tst.l	_AudioHit(A5)			; If already fired up audio...
	bne.s	.out

	lea	_IRQCause(A5),A1
	movea.l	_SysBase(A5),A6
	jsr	_LVOCause(A6)			; Start audio hardware once

.out
	lea	$DFF000,A0			; In case of a VBlank IRQ...
	moveq	#0,D0				; Set Z-Flag
	rts

*
** D0/D1/A0/A1/A5 scratch registers - A6 must remain intact
*
_IRQCauseCode	; Executed each ~1/16688 second - regardless what for a machine and which power supply!
	movea.l	A1,A5				; IS_DATA

	tst.l	_AudioHit(A5)			; Already fired up audio channel 0?
	bne.s	.out2				; If so...

	bsr.s	.startaudio

.out2
	moveq	#0,D0				; Ready and out
	rts


.startaudio
	movea.l	_Memory(A5),A0			; Address CHIP memory
	lea	_custom,A1			; DMA hardware address

	move.l	A0,aud0+ac_ptr(A1)		; Address memory
	move.w	#AUDSIZE/2,aud0+ac_len(A1)	; Amount in words of data
	move.w	#214,aud0+ac_per(A1)		; Period 214 ~= 16688 Hz, "C-3"
	move.w	#64,aud0+ac_vol(A1)		; Full volume

	move.l	A0,aud1+ac_ptr(A1)		; Address memory
	move.w	#AUDSIZE/2,aud1+ac_len(A1)	; Amount in words of data
	move.w	#214,aud1+ac_per(A1)		; Period 214 ~= 16688 Hz, "C-3"
	move.w	#64,aud1+ac_vol(A1)		; Full volume

	move.l	A0,aud2+ac_ptr(A1)		; Address memory
	move.w	#AUDSIZE/2,aud2+ac_len(A1)	; Amount in words of data
	move.w	#214,aud2+ac_per(A1)		; Period 214 ~= 16688 Hz, "C-3"
	move.w	#64,aud2+ac_vol(A1)		; Full volume

	move.l	A0,aud3+ac_ptr(A1)		; Address memory
	move.w	#AUDSIZE/2,aud3+ac_len(A1)	; Amount in words of data
	move.w	#214,aud3+ac_per(A1)		; Period 214 ~= 16688 Hz, "C-3"
	move.w	#64,aud3+ac_vol(A1)		; Full volume

	move.w	#$800F,dmacon(A1)		; Turn on audio channels 0 - 4

	st.b	D0
	move.l	D0,_AudioHit(A5)		; Indicate audio started
	rts


_PAL_NTSC_Times
	dc.w	42,43		709379/16688 = ~42, 715909/16688 = ~43	(1/16688 second till next
*				IRQ will be raised)
_IRQName
	dc.b	'AudioGrabber',0
_CIABName
	dc.b	'ciab.resource',0

	END


Wenn’s jetzt nicht funktioniert, schmeiß den Assembler weg.


Gruß

[ - Antworten - Zitieren - Direktlink - ]

03.11.2007, 10:05 Uhr

MaikG
Posts: 5172
Nutzer
Habs probiert, ILLEGAL OPERATOR sagt der Assembler und Markiert
dabei EQU 398

Die Symikolons hatte ich auch gesetzt.

[ - Antworten - Zitieren - Direktlink - ]

03.11.2007, 12:07 Uhr

jolo
Posts: 110
Nutzer
Hi Maik,

ich kann Dir nicht behilflich sein, Deinen Assembler Motorola Syntax tauglich zu machen.
EQU, equ oder die Kurzschreibweise "=" müssen von allen Assemblern, die für den Amiga geeignet sein sollen, unterstützt werden.
Die Ausnahmen sind Assembler der SEKA (nee, hat nichts mit der legendären Darstellerin zu tun :) Reihe, die eigentlich nur für diejenigen geeignet sind, die ohne das Betriebssystem programmieren wollen, sprich Hardware-Banging.

Commodore hat die Include-Dateien auf den MetaComCo-Assembler zugeschnitten und dieser ist Hundertprozent konform zum Motorola Syntax. Jeder Amiga-Assembler muss abwärtskompatibel zu diesem Assembler sein, ansonsten kann man die Include-Dateien nicht verwenden.
Leider scheint ASM-Pro (oder wie er auch immer heißt) diesem Standard nicht zu entsprechen - und ich kann Dir nur ans Herz legen, diesen nicht mehr zu benutzen.

Als Beispiele für gute Assembler:
Devpac (kommerziell)
SNMA (freeware)
PhxAss (freeware)

Es gibt bestimmt ein Dutzend mehr, jedoch habe ich diese nie selber getestet.

Um in anderen Sprachen als Basic zu programmieren, musst Du zuerst eine fehlerfreie Umgebung der gewählten Sprache schaffen. Das bedeutet aber auch, dass man das Handbuch oder was auch immer mitgeliefert wird, liest.

Um das Beispiel zu assemblieren, reicht es aber schon aus, A68k und BLink zu verwenden. Allerdings ist A68k ein reiner m68k Assembler und hat zudem noch Probleme mit den Bitfield-Makros.
Zum Assemblieren des Beispiels ist es aber ausreichend.

A68k (Assembler) -> http://aminet.net/dev/asm/A68kGibbs.lha
BLink (Linker) -> http://aminet.net/dev/misc/blink67.lzh

SNMA (Assembler) -> http://aminet.net/dev/asm/snma_2_11.lha

PhxAss (Assembler) -> http://aminet.net/dev/asm/PhxAss.lha
PhxLnk (Linker) -> http://aminet.net/dev/asm/PhxLnk432.lha


Anbei, hast Du das NDK3.9 (SDK_OS3) installiert? Wenn ja, hast Du vbcc neu installiert? Dann solltest Du nämlich keine Schwierigkeiten haben, die C-Beispiele zu kompilieren. Eine Neu-Installation von vbcc dauert keine 2 Minuten.


Gruß


Anbei, was mir gerade so einfällt:
SEKA benutzte eine andere Syntax für Wertzuweisungen:

"AUDSIZE: = 398"
anstatt
"AUDSIZE EQU 398"

Gehört ASM-Pro zur SEKA Familie (Nachfolger)?

[ - Antworten - Zitieren - Direktlink - ]

03.11.2007, 17:56 Uhr

uho
Posts: 114
Nutzer
@MaikG:

Hier handelt es sich um einen ganz simplen Fehler:

Assembler-Direktiven (Befehle, die für den Assembler, nicht
für den Prozessor sind), müssen in der ersten Spalte - also
ganz links - stehen.

Die angegebene Fehlermeldung erscheint, wenn Leerzeichen und/
oder Tabs davorstehen.

Übrigens ist AsmOne (bzw. dessen Grafikkarten-fähiges Pedant
AsmPro) nicht einfach nur ein Assembler, sondern eine komplette
Entwicklungsungebung mit grafischer Oberfläche, Monitor,
Debugger und Vielem mehr !
Dabei besteht er nur aus einer einzigen Datei (plus Config)
und kann dadurch praktisch auf jedem Amiga-System bei gleich-
zeitig sehr ergonomischer Benutzführung verwendet werden.

Fazit: Unbedingt anschauen !



@jolo: Alles ganz falsche Vermutungen ;-)


Gruß

uho

[ - Antworten - Zitieren - Direktlink - ]

03.11.2007, 19:12 Uhr

MaikG
Posts: 5172
Nutzer
@Uho

Ja, das wars. Hab angefangen das zu ändern.


Bleibt jetzt aber da stehen:

beq.s 1$

Mit "Relative Mode Error".

[ - Antworten - Zitieren - Direktlink - ]

05.11.2007, 12:31 Uhr

Holger
Posts: 8116
Nutzer
Zitat:
Original von MaikG:
Bleibt jetzt aber da stehen:

beq.s 1$

Mit "Relative Mode Error".


Versuchs mal, in dem Du den Bezeichner "1$" in ".l1" umbenennst. Musst Du natürlich an beiden Stellen machen, dem von Dir zitierten Sprungbefehl und fünf Zeilen tiefer, wo er definiert wird.

mfg

--
Good coders do not comment. What was hard to write should be hard to read too.

[ - Antworten - Zitieren - Direktlink - ]

07.11.2007, 23:19 Uhr

jolo
Posts: 110
Nutzer
@uho:
Zitat:
Übrigens ist AsmOne (bzw. dessen Grafikkarten-fähiges Pedant
AsmPro) nicht einfach nur ein Assembler, sondern eine komplette
Entwicklungsungebung mit grafischer Oberfläche, Monitor,
Debugger und Vielem mehr !
Dabei besteht er nur aus einer einzigen Datei (plus Config)
und kann dadurch praktisch auf jedem Amiga-System bei gleich-
zeitig sehr ergonomischer Benutzführung verwendet werden.

Fazit: Unbedingt anschauen !


Nee, Danke. Seit 1992 benutze ich ausschließlich Devpac 3 für meine Assembler-Projekte.
Devpac heißt die gesamte IDE, GenAm ist der Assembler, MonAm der Debugger und der Devpac nennt sich auch der Texteditor und es ist wohl das Beste kommerzielle Assembler Paket, dass je für den Amiga entwickelt wurde.
Vorher habe ich wie viele andere auch den ASSEM-Assembler (MetaComCo) und ALink verwendet, als dann SEKA rauskam, den SEKA-Assembler, der war sehr viel schneller als die Verbindung ASSEM und ALink - und man brauchte nur eine Diskette, auf der die System-Dateien (S, C, LIBS etc. - die Workbench war ja was für Memmen ;) ) sowie der Assembler Platz fanden. Das waren noch Zeiten... :)


Zitat:
@jolo: Alles ganz falsche Vermutungen ;-)

Yep, damit habe ich nicht gerechnet... :)
Ich habe vorausgesetzt, dass Maik schon eigenständig Programme in Assembler verfasst hätte.
Mein Fehler...


Gruß

[ - Antworten - Zitieren - Direktlink - ]

07.11.2007, 23:22 Uhr

jolo
Posts: 110
Nutzer
@MaikG:

Hi Maik,

Kannst Du das Beispiel mittels Holgers Hinweis assemblieren?


Gruß

[ - Antworten - Zitieren - Direktlink - ]

07.11.2007, 23:40 Uhr

MaikG
Posts: 5172
Nutzer
>Kannst Du das Beispiel mittels Holgers Hinweis assemblieren?

Ja, geht. Naja bis auf diese click-störungen.
Jetzt muss ich das ganze noch auf eine bessere Frequenz bringen
und herrausfinden wie man ein CTRL-C an das Programm schickt.

Am besten währe natürlich das vollständige Verstehen.
Momentan hab ich herrausgefunden das das timer.device
unter einem 030er noch nichtmal mit 1500 HZ korrekt arbeitet...

[ - Antworten - Zitieren - Direktlink - ]

08.11.2007, 19:35 Uhr

jolo
Posts: 110
Nutzer
@MaikG:

Hi,

Zitat:
Kannst Du das Beispiel mittels Holgers Hinweis assemblieren?

Zitat:
Ja, geht. Naja bis auf diese click-störungen.


Diese Störungen sind nur während der Datenaufzeichnung und dem gleichzeitigen Abspielen hörbar.
Es hat nichts mit der Aufzeichnung selber zu tun. Da der CIA-B Timer nicht hundertprozentig im Takt mit der Audio-Ausgabe läuft, kommt es zu Überschneidungen, die diese Störgeräusche verursachen. Ein größerer CHIP-RAM Puffer für die Audio-Hardware ("AUDSIZE EQU 65535") wirkt dem entgegen sowie die Leseintervalle um einen Tick erhöhen (41 anstatt 42 (_PAL_NTSC_TIMES)) - denn wenn die Audio-Hardware schneller Daten ausgibt als der CIA-B-Interrupt Daten liest, kommt es unweigerlich zu Störungen - aber wie gesagt, diese Störungen hörst Du nur während des Digitalisierens - nicht beim Abspielen der aufgezeichneten Daten zu einem späteren Zeitpunkt. Nochmals, diese Knackgeräusche werden nicht aufgezeichnet!
Ach ja, Du kannst STARTOFFSET auch einen größeren Wert zuweisen - dann startet die Audio-Ausgabe verzögert und somit stehen mehr Daten bereit, bevor eine hörbare Ausgabe erfolgt (kannst ja mal die halbe Größe von AUDSIZE probieren).
STARTOFFSET ist hier zu finden:
code:
Original:
	cmpi.l	#4,D0	; Indicator for start audio (leading bytes)
	bne.s	.out	; If not...
in
	cmpi.l	#AUDSIZE/2,D0	; Indicator for start audio (leading bytes)
	bne.s	.out	; If not...
ändern.



Zitat:
Jetzt muss ich das ganze noch auf eine bessere Frequenz bringen [...]

Da die maximale Sampling-Frequenz 28801 Hertz beträgt (Hardware-Limit), entspricht dieser Wert einer Periode von 124 (für die Audio-Hardware müssen Hertz-Werte in Perioden-Werte umgerechnet werden) - also musst Du im Beispiel 214 durch 124 ( 1 / (28801 * 0.000000279365) = 124,28 ) ersetzen - und das für alle vier Audio-Kanäle.

Zudem musst Du die Leseintervalle verkürzen:
_PAL_NTSC_TIMES 25,25 -> 709379/28801 bzw. für 60 Hz-Netzspannungen: 715909/28801

Anbei, wieso sind 16000 Hz nicht ausreichend? Ich habe früher mit 14000 Hz digitalisiert - und zwar ganze Musikstücke und diese sind selbst nach heutigen Standards noch ganz manierlich anzuhören.


Zitat:
[...] und herrausfinden wie man ein CTRL-C an das Programm schickt.

Selbst das Senden eines CTRL-C Signals bringt Dir alleine rein gar nichts - weißt Du wo die Daten liegen, damit diese von Deinem Hauptprogramm aus gelesen werden können? Und, gibt es überhaupt einen Datenbereich? Weiterhin, was passiert, wenn das Programm ein CTRL-C Signal empfängt?
Das Beispielprogramm (ASM) in seiner jetzigen Form ist dafür gänzlich ungeeignet - das zweite, in C verfasste Beispiel (AudioGrabber2) schon eher.


Zitat:
Am besten währe natürlich das vollständige Verstehen.

Solange Du keine fehlerfreie Installation von vbcc hast, wird das nichts.

Ich kann Dir alternativ anbieten, eine Shared-Library für Dein Problem zu programmieren - falls Du diese von Basic aus benutzen kannst.
Allerdings kann ich schon jetzt sagen, dass ich dies diese Woche nicht mehr schaffe.


Zitat:
Momentan hab ich herrausgefunden das das timer.device unter einem 030er noch nichtmal mit 1500 HZ korrekt arbeitet...

Das ist doch schon mal ein Anfang. :)

Frage: Meinst Du auf Prozess/Task-Ebene oder das Beispiel mit dem Software-Interrupt?
Was ich vergessen habe zu erwähnen ist, dass der Agnus-Baustein bedingt durch das Zusammenspiel aller Hardware-Komponenten nur in festgelegten Intervallen Software-Interrupts auslösen darf/kann. Weil ich mich aber nie damit tiefer beschäftigt habe, gehe ich davon aus, dass nur entsprechend der Bildschirmwiederholfrequenz Software-Interrupts ausgelöst werden. Um das zu verifizieren müsste ich aber erstmal das Hardware-Reference-Manual wieder finden... (dem Betreffenden, der eine abfällige Bemerkung über meinen Ordnungssinn macht, haue ich die Kartoffel vom Kopf ;) ).
Wie gesagt, ich benutze Software-Interrupts in abgewandelter Form - bei mir wird 50-mal in der Sekunde eine Aktion durchgeführt - und nicht 14000 oder gar 28000-mal. Das überfordert den Agnus-Chip.

Es würde mich interessieren, ob unter MorphOS/OS4 diese Einschränkung weiterhin besteht.


Auf Prozess/Task-Ebene wage ich zu bezweifeln, ob Du das Timing überhaupt in den Griff bekommst - zu viele Faktoren spielen hier eine Rolle.


Gruß

[ - Antworten - Zitieren - Direktlink - ]

08.11.2007, 22:35 Uhr

whose
Posts: 2156
Nutzer
@jolo:

Ich hatte ihm schon einmal zu verstehen gegeben, daß das Multitasking da eine Rolle spielen kann. Insofern sind Deine Interrupt-Beispiele sicherlich etwas besser "zu gebrauchen". Vor allem das mit dem Timer-Interrupt, was wohl auch bei der "normalen" Sampler-Software benutzt wird.

In Sachen Verständnis hätte evtl. auch ein Hinweis auf den VBI helfen können, dessen Beispielprogramm im RKM ich heute "wiederentdeckt" habe. Sehr trivial, wie ich fand, und für das Verständnis der Feinheiten, auf die vorher schon eingegangen wird, auch recht hilfreich.

Was ich allerdings bis heute nicht begreife ist, wie er so schlechte Ergebnisse bekommen kann. Auf meinen Maschinen hier ist das timer.device auch bei recht kurzen Intervallen "stabil" genug, um nicht dermaßen gigantische Abweichungen zu produzieren. Insofern wäre es schon recht interessant zu erfahren, woran es bei ihm hapern könnte.

Den DTMF-Decoder können wir jetzt aber sicherlich "zum Laufen" bewegen, das Sampling ist ja nun einigermaßen "sicher" und das Decoding kann dadurch auch asynchron ablaufen (wobei ich denke, daß der Decoder selbst schnell genug sein sollte, um mit nicht allzu großem Nachlauf decodieren zu können). Ok, funktionierender vbcc oder sonstiger C-Compiler vorausgesetzt :D

Mal schaun, was noch draus wird ;)

@MaikG:

Schau Dir das Kapitel "Exec: Interrupts" im RKM nochmal an, dort vor allem das Beispiel für den VBlank-Interrupt-Server und probier damit mal etwas rum. Dann verstehst Du auch den timer- und Soft-Interrupt besser, könnte ich mir vorstellen.

Obacht: Nicht alles, was auf Anhieb zu funktionieren scheint, läuft auch wirklich sicher über nen Interrupt. Also beherzige die Warnungen, die im RKM auftauchen, dann hast Du später weniger zu meckern ;)

Grüße

--
---

:boing: µA1 PPC 750GX-800
:boing: A4000 PPC 604e-233

[ - Antworten - Zitieren - Direktlink - ]


1 -2- 3 4 [ - Beitrag schreiben - ]


amiga-news.de Forum > Programmierung > Registerzugriff in C [ - Suche - Neue Beiträge - Registrieren - Login - ]


.
Impressum | Datenschutzerklärung | Netiquette | Werbung | Kontakt
Copyright © 1998-2024 by amiga-news.de - alle Rechte vorbehalten.
.