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 - ]

11.10.2007, 16:05 Uhr

MaikG
Posts: 5172
Nutzer
Wollte grade nochmal das eclock teil in C mit einem Sampling
code ausstatten um zu sehen wie weit C da kommt.

Ich bräuchte den Wert aus 0xBFE101 um 127 reduziert und per
copymem kopierbar.
Und eine Möglichkeit 0xBFE301 zu 0 zu setzten.




code:
ULONG *buffer;
  ULONG handle;
  WORD tmp;

 buffer=AllocVec(120000,MEMF_FAST);

 tmp=&0xBFE101;
 tmp=tmp-127;
 CopyMem(*tmp,buffer+repeat,1);


[ Dieser Beitrag wurde von MaikG am 11.10.2007 um 16:06 Uhr geändert. ]

[ - Antworten - Zitieren - Direktlink - ]

11.10.2007, 16:42 Uhr

Der_Wanderer
Posts: 1229
Nutzer
Hm... also das mit den Pointer müssen wir nochmal üben.

Ich bin kein Amiga-C Experte, aber würde folgendes vorschlagen:

code:
UBYTE *SampleData   = 0xBFE101;
UBYTE *SampleStrobe = 0xBFE301;
UBYTE tmp;
UBYTE *buffer;

buffer=(UBYTE*)AllocVec(120000*sizeof(UBYTE),MEMF_FAST);

While (repeat<120000) {
  tmp=*SampleData;       // Register auslesen
  tmp-=0x80;            // unsigned => signed
  buffer[repeat] = tmp; // speichern
  *SampleStrobe = 0;    // Dieses Register zu 0 setzen
  repeat++;
}



--
Thilo Köhler, Author von:
HD-Rec, Sweeper, Samplemanager, ArTKanoid, Monkeyscript, Toadies, AsteroidsTR, TuiTED, PosTED, TKPlayer, AudioConverter, ScreenCam, PerlinFX, MapEdit, TK AB3 Includes und viele mehr...
Homepage: http://www.hd-rec.de



[ Dieser Beitrag wurde von Der_Wanderer am 11.10.2007 um 16:44 Uhr geändert. ]

[ Dieser Beitrag wurde von Der_Wanderer am 11.10.2007 um 17:01 Uhr geändert. ]

[ - Antworten - Zitieren - Direktlink - ]

11.10.2007, 16:47 Uhr

DrNOP
Posts: 4118
Nutzer
Wat is' mit dem Code, den du gepostet hast? Der funktioniert wohl nicht, oder?

Ich hab' grade mal ein Stück Hardware-Definitionen aus einem unserer hiesigen Boards rauskopiert:
C code:
/* Local I/O addresses */
#define	DUSCC_BASE_ADRS	((char *) 0x02000000) /* m68562 DUSCC */
#define	QUART_BASE_ADRS	((char *) 0x02010000) /* XR-82C684 QUART */
#define	CAN_BASE_ADRS	((char *) 0x02070000) /* AN 82C527 CAN */


Zeiger kennst du inzwischen.

Der Punkt ist:
Die Zeiger, die du bisher benutzt hast waren Variablen. Die hatten einen Namen, weil du die aktuelle Adresse üblicherweise nicht kennst und sie dich auch nicht interessiert. Hier hast du jetzt bereits eine fixe Adresse, auf die du zugreifen willst. Jetzt mußt du dem Compiler nur noch sagen, auf welchen Inhalt diese Adresse - sprich: dieser Zeiger - zeigt. Deshalb brauchst du einen Cast, in meinem Beispiel ist das das "(char *)". Wenn dein Register länge ist, mußt du eben auf einen int oder longint oder sowas casten. Vielleicht lohnt sich auch ein unsigned davor.

Damit das Programm besser lesbar ist, empfiehlt sich vielleicht ein define, damit die Adresse auch einen Namen bekommt.

Um das Ding dann zu benutzen kannst du wie gewohnt eine Zeigervariable anlegen. In meinem Beispiel sind das diese:
C code:
volatile char * duscc;
volatile char * quart;
volatile char * can;


Die Zuweisung, damit die Variablen auch auf die passenden Adressen zeigen, ist diese:
C code:
duscc    = DUSCC_BASE_ADRS;  /* base pointer to DUSCC    */
quart    = QUART_BASE_ADRS;  /* base pointer to QUART    */
can      = CAN_BASE_ADRS;    /* base pointer to CAN      */


--
Signaturen mit mehr als zwei Zeilen gehen mir auf den Wecker

[ - Antworten - Zitieren - Direktlink - ]

11.10.2007, 18:11 Uhr

MaikG
Posts: 5172
Nutzer
>ich bin kein Amiga-C Experte, aber würde folgendes vorschlagen:

> UBYTE *SampleData = 0xBFE101;
> UBYTE *SampleStrobe = 0xBFE301;

wird nicht aktzeptiert UBYTE stimmt nicht mit int überein.


>Ich hab' grade mal ein Stück Hardware-Definitionen aus einem unserer hiesigen Boards rauskopiert:
> C code:
> /* Local I/O addresses */
> #define DUSCC_BASE_ADRS ((char *) 0x02000000) /* m68562 DUSCC */

Sowas wird auch nicht aktzeptiert, da sagt der Compiler(SASC) das
diese definition ohne wirkung ist.

[ - Antworten - Zitieren - Direktlink - ]

11.10.2007, 18:15 Uhr

platon42
Posts: 400
[Ex-Mitglied]
Zitat:
Original von MaikG:
Wollte grade nochmal das eclock teil in C mit einem Sampling
code ausstatten um zu sehen wie weit C da kommt.

Ich bräuchte den Wert aus 0xBFE101 um 127 reduziert und per
copymem kopierbar.
Und eine Möglichkeit 0xBFE301 zu 0 zu setzten.

code:
ULONG *buffer;
  ULONG handle;
  WORD tmp;

 buffer=AllocVec(120000,MEMF_FAST);

 tmp=&0xBFE101;
 tmp=tmp-127;
 CopyMem(*tmp,buffer+repeat,1);



Du solltest von Hardware-Registern die Finger lassen, zumindest mit
OS-Funktionen wie CopyMem, denn Du weißt nicht, was der damit macht (es
gibt keine Garantie, dass der da jetzt byteweise zugreift).

Und MEMF_FAST ist eine Unsitte, bitte gleich streichen. Außerdem solltest
Du vorher mit der ciaa.resource die Portbits allokieren und später wieder
freigeben.

Richtigerweise sollte das eher so aussehen:

code:
include <hardware/cia.h>

   UBYTE *buffer; // du willst keine ULONGs, oder?

   // hardware register sind unbedingt als volatile zu kennzeichnen,
   // sonst optimiert der compiler dir den zugriff weg!
   volatile struct CIA *ciaa = (volatile struct CIA *) 0xbfe001;

   // das ist ne optimierung... aber prinzipiell könntest 
   // Du unten auch mit x = ciaa->ciaprb arbeiten
   volatile UBYTE *ciaaportb = &ciaa->ciaprb; 
   ULONG cnt = 120000;
   UBYTE *targetptr;

   // wenn du nicht im interrupt läufst. Wenn Du später mal deine
   // Auslese mit exaktem Timing brauchst, wirst Du das sicher in
   // einen Interrupt auslagern, dann sollte MEMF_PUBLIC gesetzt sein

   targetptr = buffer = (UBYTE *) AllocVec(120000, 0);

   do
   {
       *targetptr++ = *ciaaportb - 127; // -127? Thilo hatte bei sich -128 (0x80) stehen.

       // do while(), nur wenn du weißt, dass cnt sicher größer als 0
       // ist (wie in diesem Fall); eine while() {} schleife erzeugt
       // teilweise schlechteren code, ebenso eine "vorwärtsschleife"
       // mit cnt = 0; cnt < 120000; cnt++)
   } while(--cnt); 
   ciaa->ciaddrb = 0x55; // 0xbfe301 beschreiben



--
Best Regards

Chris Hodges

[ Dieser Beitrag wurde von platon42 am 11.10.2007 um 18:18 Uhr geändert. ]

[ - Antworten - Zitieren - Direktlink - ]

11.10.2007, 18:19 Uhr

Mad_Dog
Posts: 1944
Nutzer
Hallo Mike,

Schau Dir mal den Code von meinem Programm "Monoscope" an, da ist alles drin, was Du suchst (schließlich habe ich es damals extra wegen Deiner Frage geschrieben):

http://w3.norman-interactive.com/Monoscope_1_3.lha

BTW: Ein Anwendungsprogramm sollte normalerweise NIE auf irgendwelche Register zugreifen. Mein Programm tut's trotzdem, weil ich so nah wie möglich an dem bleiben wollte, was Du in BASIC programmiert hast.

Normalerweise greift ein Anwendungsprogramm bei "sauberer" Programmierung nur über Devices oder Libraries auf Hardware zu und nie direkt. Direkte Low-Level Zugriffe führen oft zu Inkompatiblitäten, sobald sich was an der Hardware ändert (vgl. OCS vs. AGA).

--
http://www.norman-interactive.com

[ - Antworten - Zitieren - Direktlink - ]

11.10.2007, 21:29 Uhr

MaikG
Posts: 5172
Nutzer
>richtigerweise sollte das eher so aussehen:

ich glaubt SASC verkraftet da so einige neuere sachen nicht.


// wenn du nicht im interrupt läufst. Wenn Du später mal deine
// Auslese mit exaktem Timing brauchst, wirst Du das sicher in
// einen Interrupt auslagern, dann sollte MEMF_PUBLIC gesetzt sein


Von Interrupts habe ich fast 0 Ahnung, ich würde ja gerne.
Das timer.device bringt keine aktzeptablen Frequenzen.


>Schau Dir mal den Code von meinem Programm "Monoscope" an, da ist
>alles drin, was Du suchst (schließlich habe ich es damals extra
>wegen Deiner Frage geschrieben):

Ja, hab da einfach mal parallel.h von genommen.
Dann statt dieses Array den AllocVec buffer genommen(-127 wird halt
nicht gemacht).
Später dann mit nem Sampleprog entsprechend geändert.

Ergebniss: Ernüchternd ab 12000 HZ gibts Mikimaus stimmen,
wird also weniger gesampelt als erforderlich.
Mit MaxonBasic erreiche ich die selbe Geschwindigkeit.

[ - Antworten - Zitieren - Direktlink - ]

11.10.2007, 22:31 Uhr

platon42
Posts: 400
[Ex-Mitglied]
Zitat:
Original von MaikG:
>richtigerweise sollte das eher so aussehen:

ich glaubt SASC verkraftet da so einige neuere sachen nicht.


Der Code ist SAS/C kompatibel. Welche "neueren Sachen" sollte er denn nicht können?

Zitat:
// wenn du nicht im interrupt läufst. Wenn Du später mal deine
// Auslese mit exaktem Timing brauchst, wirst Du das sicher in
// einen Interrupt auslagern, dann sollte MEMF_PUBLIC gesetzt sein

Von Interrupts habe ich fast 0 Ahnung, ich würde ja gerne.
Das timer.device bringt keine aktzeptablen Frequenzen.


Die LowLevel-Library bietet ein paar sehr einfache Funktionen, um CIA-Timer-Interrupts einzubinden. Und das timer.device ist mit UNIT_MICROHZ recht akzeptabel -- leider bei mir nicht 100% regelmäßig.

Zitat:
> Ergebniss: Ernüchternd ab 12000 HZ gibts Mikimaus stimmen,
> wird also weniger gesampelt als erforderlich.


Seltsam. Selbst ein 68000er ist fähig, mit mehr als 28000Hz zu samplen. Zumindest in ASM.
--
--
Best Regards

Chris Hodges

[ - Antworten - Zitieren - Direktlink - ]

12.10.2007, 10:04 Uhr

MaikG
Posts: 5172
Nutzer
>Der Code ist SAS/C kompatibel. Welche "neueren Sachen" sollte er
>denn nicht können?

Vielleicht a = b =, es kamen auf einmal eine ganze reihe
fehlermeldungen. Vielleicht hab ich mich auch irgendwo vertippt.

>Die LowLevel-Library bietet ein paar sehr einfache Funktionen,
>um CIA-Timer-Interrupts einzubinden.

Muss ich mir mal die Autodocs zu ansehen.

>Und das timer.device ist mit UNIT_MICROHZ recht akzeptabel --
>leider bei mir nicht 100% regelmäßig.

Wenn ich Zeitgleich nur eine kleinigkeit mache habe ich ein
leiern. Wenn ich ein paar sekunden nur Puffere komme ich auf
max. 12000 HZ.


>Seltsam. Selbst ein 68000er ist fähig, mit mehr als 28000Hz zu
>samplen. Zumindest in ASM.

Ja, wenn C angeblich schon fast so schnell sein soll wie ASM
müssten 28000 HZ auf einem 68060 mindestens drin sein.

Megalosound(AMOS) macht 44 khz und kann sogar in Echtzeit noch Echo
etc. hinzufügen, was schon bei 68030 sehr gut geht.

[ - Antworten - Zitieren - Direktlink - ]

12.10.2007, 11:02 Uhr

Der_Wanderer
Posts: 1229
Nutzer
@MaikG

Du machst halt irgenwo was falsch.
Selbst ein A500 kann höher sampeln als 12kHz.
An C liegt das sicher nicht.

Bei meinem Code habe ich vergessen, dass du das Register
noch casten musst, also

code:
UBYTE *SampleData   = (UBYTE*)0xBFE101;
UBYTE *SampleStrobe = (UBYTE*)0xBFE301;
...


Aber hör mal, wenn dich sowas davon abhält, den Code
zu Laufen zu bringen, dann solltest du dir erstmal
ein C Buch kaufen und ein wenig lesen.

Wenn es leiert mit dem timer.device, dann solltest du
die TaskPri hochsezten, am besten über das input.device,
also 20 oder höher. Dann leiert nichts mehr.

Das timer.device bringt durchaus gutes Timing, wenn man
es richtig anspricht. Auf einem 60er (? nutzt du einen 60er?)
geht das auf jeden Fall, auch mit 44.1kHz.

Es liegt NICHT an C und auch nicht am timer.device.
Wenn es nicht klappt, dann machst du was falsch.

In Amiblitz könntest du das auch einfach zusammencoden.
Früher hatte ich z.B. einen CIA Chip getriebenen Audio
Treiber in HD-Rec gecoded, um mit 44.1kHz/14bit Paula
abspielen zu können. Ging auch ohne Probleme und hat nicht
geleiert.



--
Thilo Köhler, Author von:
HD-Rec, Sweeper, Samplemanager, ArTKanoid, Monkeyscript, Toadies, AsteroidsTR, TuiTED, PosTED, TKPlayer, AudioConverter, ScreenCam, PerlinFX, MapEdit, TK AB3 Includes und viele mehr...
Homepage: http://www.hd-rec.de


[ - Antworten - Zitieren - Direktlink - ]

12.10.2007, 13:10 Uhr

Holger
Posts: 8089
Nutzer
Zitat:
Original von Der_Wanderer:
Es liegt NICHT an C und auch nicht am timer.device.
Wenn es nicht klappt, dann machst du was falsch.

Na das ist doch mal ein toller Spruch. Er fragt ja schließlich schon seit Monaten, was er falsch macht, ohne dass es ihm einer sagen konnte. Wenn Du es weißt, dann raus mit der Sprache.
Zitat:
In Amiblitz könntest du das auch einfach zusammencoden.
Früher hatte ich z.B. einen CIA Chip getriebenen Audio
Treiber in HD-Rec gecoded, um mit 44.1kHz/14bit Paula
abspielen zu können. Ging auch ohne Probleme und hat nicht
geleiert.

Also mit direkten Hardwarezugriff, statt timer.device, les ich das richtig?

Dass es "eigentlich gehen müsste", braucht ihm keiner zu sagen, das hat er selber schon vermutet und mehrfach geschrieben.

mfg

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

[ - Antworten - Zitieren - Direktlink - ]

12.10.2007, 14:22 Uhr

MaikG
Posts: 5172
Nutzer
>Na das ist doch mal ein toller Spruch. Er fragt ja schließlich
>schon seit Monaten, was er falsch macht, ohne dass es ihm einer
>sagen konnte. Wenn Du es weißt, dann raus mit der Sprache.

Genau genommen mit dem DTMF-goertzel schon seit nem Jahr...


>Also mit direkten Hardwarezugriff, statt timer.device, les ich das
>richtig?

Ich kann nur mit dem timer.device, CIA - keinen schimmer.

Abspielen ist auch nicht das problem, das macht die Hardware
schon selber auch auf 000.

Mit der Taskpriorität habe ich schon alles durch, ebenfalls
die verwendung von forbid/permit was das ganze nur minimal verbessert.


Hier ist nochmal das komplette eclock mit sampling:

code:
#include <stdio.h>  
    #include <exec/exec.h>
    #include <exec/ports.h>
    #include <exec/libraries.h>
    #include <dos/dos.h>
    #include <devices/timer.h>

    #include "parallel.h"
    #include <proto/all.h>
    #include <proto/dos.h>
    #include <proto/exec.h>


    struct timerequest *TimerIO;
    struct MsgPort *TimerMP;
    struct Message *TimerMsg;
    struct Library *TimerBase;

    int main(void)
    {
        struct timeval ref1, ref2;
        struct EClockVal eclockval;
        unsigned long intervall, ticks;
        long diff;
        LONG error;
        ULONG handle;
        UBYTE *buffer;
        unsigned int repeat = 0;
        unsigned int sekunden = 5;
        buffer = AllocVec(120000, MEMF_PUBLIC);

        TimerMP = (struct MsgPort *)CreatePort(NULL, 0);
        if(!TimerMP)
        {
            printf("Konnte Timer-Port nicht anlegen!\n");
            return(RETURN_FAIL);
        }

        TimerIO = (struct timerequest *)CreateExtIO(TimerMP, sizeof(struct timerequest));
        if(!TimerIO)
        {
            printf("Konnte Timer-IORequest nicht anlegen!\n");
            return(RETURN_FAIL);
        }

        error = OpenDevice(TIMERNAME, UNIT_WAITECLOCK, (struct IORequest *)TimerIO, 0L);
        if(error)
        {
            printf("Konnte timer.device nicht öffnen!\n");
            return(RETURN_FAIL);
        }
        
        TimerBase = (struct Library *)TimerIO->tr_node.io_Device;
        SET_PARALLEL_PORT_READ;

        printf("Gewünschtes Intervall in Hz: ");
        scanf("%lu", &intervall);

        ticks = ReadEClock(&eclockval);
        diff = ticks / intervall; /* Normal müßte man auf - testen */

        printf("Hi: %lu Lo: %lu\n", eclockval.ev_hi, eclockval.ev_lo);

        /* sehr simpler Überlauf-Test */
        if( ((eclockval.ev_lo) + diff) < eclockval.ev_lo)
        {
            eclockval.ev_hi++;
        }
        else
        {
            eclockval.ev_lo += diff;
        }


        TimerIO->tr_node.io_Command = TR_ADDREQUEST;
        TimerIO->tr_time.tv_secs = eclockval.ev_hi;
        TimerIO->tr_time.tv_micro = eclockval.ev_lo;

        SendIO((struct IORequest *)TimerIO);

        GetSysTime(&ref1);

        while(repeat < intervall*sekunden)
        {
            TimerMsg = WaitPort(TimerMP);
            
            /* Vorsicht, wilde Überläufe ;)  */
            if( ((eclockval.ev_lo) + diff) < eclockval.ev_lo)
            {
                eclockval.ev_hi++;
            }
            else
            {
                eclockval.ev_lo += diff;
            }
            TimerIO->tr_node.io_Command = TR_ADDREQUEST;
            TimerIO->tr_time.tv_secs = eclockval.ev_hi;
            TimerIO->tr_time.tv_micro = eclockval.ev_lo;
            SendIO((struct IORequest *)TimerIO);
            READ_PAR(*buffer+repeat);  // Wert vom Parallelport kopieren
            repeat++;
        }    

        GetSysTime(&ref2);


        AbortIO((struct IORequest *)TimerIO);
        WaitIO((struct IORequest *)TimerIO);  

handle=Open("t:sample",1006);
if(handle) {
    Write(handle,buffer,120000);
    Close(handle);
}

        FreeVec(buffer); 

        CloseDevice((struct IORequest *)TimerIO);
        DeleteExtIO((struct IORequest *)TimerIO);
        DeletePort(TimerMP);

        printf("\n");
        printf("Intervall(EClock-ticks): %ld\n", ticks / intervall);
        printf("Referenz 1: %ld %ld\n", ref1.tv_secs, ref1.tv_micro);
        printf("Referenz 2: %ld %ld\n", ref2.tv_secs, ref2.tv_micro);
        SubTime(&ref2, &ref1);
        printf("Differenz:  %9ld %ld\n", ref2.tv_secs, ref2.tv_micro);
        
        return(RETURN_OK);
    }


Dann habe ich noch den Code wie von Monoscope benutzt,
allerdings geht der schlechter.

[ - Antworten - Zitieren - Direktlink - ]

13.10.2007, 05:51 Uhr

Mad_Dog
Posts: 1944
Nutzer
Hallo Mike,

Sorry, aber ich habe nicht die Zeit gefunden, Deinen Code anzutesten, weil ich anderweitig beschäftigt war (muß meine Kröten auch durch Programmieren verdienen).

Aber mir kam da eine Idee, wie Du Dein Problem (Erkennung von Tonwahlfrequenzen) auch anders lösen könntest, als durch die Kombination Sampler / softwaremäßige Frequenzanalyse.

Ich hab ein wenig gegoogelt und bin auf diesen Baustein gestoßen:

http://www.conrad.de/Elektronik-Messtechnik/mt_8870_dcd.sap

Dieser Decoder macht die Frequenzanalyse hardwaremäßig.
Das zu schreibende Programm wäre dann super einfach - einfach 4 Bits am Parallelport abfragen, verzweigen, fertig. Damit gäbe es auch kein Timing Hick hack und der Rechner verschwendet nicht so viel Rechenzeit, weil der Decoder die Töne ja alleine erkennt...

Wenn Du diesen Baustein anstatt dem Sampler an den Parallelport hängst,
könntest Du ganz bequem anhand von 4 Bits auslesen, welcher Ton (bzw. welche Taste) es denn war. Dazu wäre allerdings ein wenig Bastelarbeit angesagt - du müsstest Dir eben eine kleine Platine mit dem Baustein und nem entsprechenden Sub-D Stecker zusammenlöten, aber das sollte selbst für den "nicht Lötprofi" kein allzugroßes Problem sein.

--
http://www.norman-interactive.com

[ - Antworten - Zitieren - Direktlink - ]

13.10.2007, 10:07 Uhr

MaikG
Posts: 5172
Nutzer
>Aber mir kam da eine Idee, wie Du Dein Problem (Erkennung von Tonwahlfrequenzen) auch anders lösen könntest, als durch die Kombination Sampler / softwaremäßige Frequenzanalyse.


Momentan gehts mehr ums Samplen selbst.


Ja, für DTMF gibt es Hardware - nur habe ich keine freien Eingänge
mehr. Den Sampler brauche ich auch für die Spracherkennung sowie
für dieses hier.

[ - Antworten - Zitieren - Direktlink - ]

13.10.2007, 16:02 Uhr

MaikG
Posts: 5172
Nutzer
So was sagt man dazu:

code:
Forbid
 WHILE repeat1&<120001 
        FOR i%=1 TO 370
        NEXT i%
        POKEB MySample&+repeat1&, PEEKB(&hBFE101)-127
        INCR repeat1&
 WEND
Permit


Erreicht locker 25000 HZ unter Basic, ohne leiern, perfekte Qualität.
Ich könnte das noch weiter Treiben wenn man die 370 reduziert.

Problem ist natürlich die Multitasking unfähigkeit, die CPU-Last
und das man die Wartezeit je nach Amiga anpassen muss.

Bewiesen ist aber, das es am timer.device liegt.

Könnte ich jetzt wenigstens irgendwo per direktzugriff auf die CIA's die
genaue Zeit auslesen, währe das Problem mit der Warteschleife weg.

[ Dieser Beitrag wurde von MaikG am 13.10.2007 um 16:03 Uhr geändert. ]

[ - Antworten - Zitieren - Direktlink - ]

13.10.2007, 17:11 Uhr

platon42
Posts: 400
[Ex-Mitglied]
@MaikG:
Das timer.device ist fürs Warten, es wartet mindestens so lange wie angegeben. Aber: Für regelmäßige, kurze Intervalle ist das ungeeignet. Wenn Du das willst, verwende die realtime.library oder eben die lowlevel.library für den CIA-Timer.

Du wirst mit diesen Task-Polling-Schleifen nichts Vernünftiges hinbekommen. Ever.
--
--
Best Regards

Chris Hodges

[ - Antworten - Zitieren - Direktlink - ]

13.10.2007, 18:14 Uhr

MaikG
Posts: 5172
Nutzer
>Wenn Du das willst, verwende die realtime.library oder eben die
>lowlevel.library für den CIA-Timer.

Die lowlevel.library ist mir zu hoch. Was macht die realtime.library?

[ - Antworten - Zitieren - Direktlink - ]

15.10.2007, 21:59 Uhr

jolo
Posts: 108
Nutzer
*** Header gekürzt, siehe http://www.amiga-news.de/forum/thread.php?id=34308&BoardID=2 (PAB) ***
C code:
/*
;: ts=4

Written entirely in the middle of the night.... 05/29/02
Last Modified: 06/03/02

Written by:
	Joerg van de Loo
		using
	MaxonC++ V4

This file is released to Amiga-C group...

*** gekürzt *** (PAB)

*/

#include	<hardware/cia.h>
#include	<hardware/custom.h>
#include	<exec/execbase.h>
#include	<exec/memory.h>
#include	<exec/interrupts.h>
#include	<dos/dos.h>

#include	<clib/cia_protos.h>
#include	<clib/exec_protos.h>


#if !defined(__MAXON__)
#include	<proto/cia.h>
#include	<proto/exec.h>
#else
#include	<pragma/cia_lib.h>
#include	<pragma/exec_lib.h>
#endif


extern struct ExecBase *Sysbase;
struct Library *CiaBase;

struct Interrupt *CIAB_IRQ;
struct Interrupt *Soft_IRQ;

#define custom ((struct Custom *) (0xDFF000))
#define ciaa ((struct CIA *) (0xBFE001))
#define ciab ((struct CIA *) (0xBFD000))

UBYTE *AudioBuffer;
ULONG AudioDataOffset = 0;
ULONG AudioHit = 0;

/* PAL: 709379 Hz / 16688 Hz = 42.50, NTSC: 715909 Hz / 16888 Hz = 42.89 */
UWORD Timeout[2] =
{
 42,43
};

/* 398 bytes required at 16688 Hz (== samples per second) for one loop...
   (16688 / 42)
*/
#define AUDIOSIZE 398

/* When 2 bytes (one sample) have been already fetched, start audio */
#define STARTOFFSET 2

/* ~PERIOD = 1/(Hz*28/10e7) :: 10e7 == 100000000
     or
   1 / (Hz * 0.000000279365)
*/

#define PERIOD 214		/* Equivalent to 16688 Hz... (" C-3 ") */
#define VOLUME 64		/* Max. volume */


UWORD PowerIndex;


#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


/* Maxon doesn't support __saveds - so apply a work-around by using large
   data model! */
#ifdef __MAXON__
#define __saveds
#endif

/* Interrupt related */
#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


const unsigned short __ret_a4_code[] =
{
	0x200C,
	0x4E75
};

/* Make above code accessible as function */
/* Need these assembler instructions here:
	move.l	A4,D0
	rts
*/
const APTR (*__ret_a4)( void) = (const APTR (*) (void)) __ret_a4_code;


/* NOTE: Only the registers D0, D1, A0 and A1 are trashed by my compiler -
   so it works.
   You  may,  of course, use the __saveds keyword to get your local base
   register and to restore the initial content.
   Unfortunately, my compiler doesn't support it.
*/

SUPERIOR ULONG CIAB_Code( REG(a1, APTR data) )
{
	char data_byte;
	LONG i;

	i = 2;
	data_byte = ciaa->ciaprb;	/* Get the data at parallel port */
	data_byte += 128;			/* Make a signed byte */

	if (AudioDataOffset < AUDIOSIZE)
	{
		AudioBuffer[AudioDataOffset] = data_byte;
		AudioDataOffset++;
	}
	else	/* Restart at first data byte in buffer */
	{
		AudioDataOffset = 0;
		AudioBuffer[AudioDataOffset] = data_byte;
		AudioDataOffset++;
	}

	if (AudioDataOffset == STARTOFFSET)
	{
		if ( !AudioHit)
			Cause( Soft_IRQ);	/* Only once called, audio hardware */
	}							/* restarts itself from beginning... */

	return i - 2;	/* For stupid compilers which don't deal with */
}					/* interrupts (MaxonC++/HiSoftC++) */

void StartAudio( void)
{
	custom->aud[0].ac_ptr = (UWORD *) AudioBuffer;
	custom->aud[0].ac_len = AUDIOSIZE / 2;	/* Amount in number of words */
	custom->aud[0].ac_per = PERIOD;
	custom->aud[0].ac_vol = VOLUME;

	custom->aud[1].ac_ptr = (UWORD *) AudioBuffer;
	custom->aud[1].ac_len = AUDIOSIZE / 2;	/* Amount in number of words */
	custom->aud[1].ac_per = PERIOD;
	custom->aud[1].ac_vol = VOLUME;

	custom->aud[2].ac_ptr = (UWORD *) AudioBuffer;
	custom->aud[2].ac_len = AUDIOSIZE / 2;	/* Amount in number of words */
	custom->aud[2].ac_per = PERIOD;
	custom->aud[2].ac_vol = VOLUME;

	custom->aud[3].ac_ptr = (UWORD *) AudioBuffer;
	custom->aud[3].ac_len = AUDIOSIZE / 2;	/* Amount in number of words */
	custom->aud[3].ac_per = PERIOD;
	custom->aud[3].ac_vol = VOLUME;

	custom->dmacon = (0x800F);				/* Start audio channels 0
											   through 3 */

	AudioHit = 1;
}


/* NOTE: Only the registers D0, D1, A0 and A1 are trashed by my compiler -
   so it works.
   You  may,  of course, use the __saveds keyword to get your local base
   register and to restore the initial content.
   Unfortunately, my compiler doesn't support it.

   From  within  a  level-6 irq (CIA-B) we can't call a level-3 irq (audio
   DMA)  so  we need to call it from a irq below of 3 - Cause() is level-1
   so it fits.
*/

SUPERIOR ULONG Soft_Code( REG(a1, APTR data) )
{
	LONG i;

	i = 2;

	StartAudio();

	return i - 2;	/* For stupid compilers which don't deal with */
}					/* interrupts (MaxonC++/HiSoftC++) */

int main( void)
{
	UBYTE icrb, cr;

	if ( (AudioBuffer = (UBYTE *) AllocMem( AUDIOSIZE, MEMF_CHIP)) )
	{
		if ( (CiaBase = (struct Library *) OpenResource( "ciab.resource")) )
		{
			if ( (CIAB_IRQ = (struct Interrupt *)
							 AllocMem( sizeof( struct Interrupt),
										MEMF_CLEAR|MEMF_PUBLIC)) )
			{
				if ( (Soft_IRQ = (struct Interrupt *)
								 AllocMem( sizeof( struct Interrupt),
											MEMF_CLEAR|MEMF_PUBLIC)) )
				{
					CIAB_IRQ->is_Node.ln_Type = (UBYTE) NT_INTERRUPT;
					CIAB_IRQ->is_Node.ln_Name = "Audio Grabber";
					CIAB_IRQ->is_Data = __ret_a4();
					#ifndef __cplusplus
					CIAB_IRQ->is_Code = (void *) &CIAB_Code;
					#else
					/* Cast operator necessary when compiled in C++ mode... */
					(void *) CIAB_IRQ->is_Code = (void *) &CIAB_Code;
					#endif
					Soft_IRQ->is_Node.ln_Type = NT_INTERRUPT;
					Soft_IRQ->is_Node.ln_Name = "Audio Grabber";
					Soft_IRQ->is_Data = __ret_a4();
					#ifndef __cplusplus
					Soft_IRQ->is_Code = (void *) &Soft_Code;	/* Ditto */
					#else
					(void *) Soft_IRQ->is_Code = (void *) &Soft_Code;
					#endif

					/* Determine basic system clock */
					if ( ((struct ExecBase *) SysBase)->PowerSupplyFrequency == 50)
						PowerIndex = 0;
					else
						PowerIndex = 1;

					/* There are only two vectors, timer A and B (0 and 1) */
					for (icrb = 0; icrb < 2; icrb++)
					{
						/* Supply vector, - established? */
						if ( !(AddICRVector( CiaBase, icrb, CIAB_IRQ)) )
							break;
					}

					/* If vector is in range ( 0 to 1 ) */
					if (icrb <= 1)
					{
						if (icrb == 0)				/* Got timer A ? */
						{
							cr = ciab->ciacra;		/* Control register timer A */
							cr &= 128;				/* Select mode (bit 7 unused) */
							cr |= 1;				/* Set timer A to start */
							ciab->ciacra = cr;
							ciab->ciaicr = 129;		/* Enable timer A */

							/* Write delay time into registers */
							ciab->ciatalo = (Timeout[PowerIndex] & 0xFF);
							ciab->ciatahi = (Timeout[PowerIndex] >> 8 & 0xFF);
						}
						else						/* Timer B */
						{
							cr = ciab->ciacrb;		/* Control register timer B */
							cr &= 128;				/* Select mode */
							cr |= 1;				/* Set timer B to start */
							ciab->ciacrb = cr;
							ciab->ciaicr = 130;		/* Enable timer B */

							/* Write delay time into registers */
							ciab->ciatblo = (Timeout[PowerIndex] & 0xFF);
							ciab->ciatbhi = (Timeout[PowerIndex] >> 8 & 0xFF);
						}

						Wait( SIGBREAKF_CTRL_C);

						custom->dmacon = 15;		/* Turn off audio */

						/* Tell system reuse allowed */
						RemICRVector( CiaBase, icrb, CIAB_IRQ);
					}

					FreeMem( Soft_IRQ, sizeof( struct Interrupt) );
				}
				FreeMem( CIAB_IRQ, sizeof( struct Interrupt) );
			}
/*			CloseResource( CiaBase);	--- not necessary and not possible!	*/
		}
		FreeMem( AudioBuffer, AUDIOSIZE);
	}

	return 0;
}



Hatte das schon mal in einer Mailing-Liste geposted - vielleicht hift's Dir weiter.

Gruß

[ - Antworten - Zitieren - Direktlink - ]

16.10.2007, 14:29 Uhr

MaikG
Posts: 5172
Nutzer
Sieht im prinzip ja interressant aus, aber weder SASC noch vbcc
Compiliert das.

[ - Antworten - Zitieren - Direktlink - ]

17.10.2007, 20:55 Uhr

jolo
Posts: 108
Nutzer
Hi,

da Deine Aussage nur so von Details strotzt, machst Du mir eine Fehleranalyse ungewöhnlich leicht...

Zu Deiner Information:
Mittels vbcc und NDK3.9 sowie einer selbst erstellten Inline-Datei (inline/cia_protos.h) kann dieses Beispiel kompiliert werde. Falls die Inline-Datei nicht vorhanden ist, muss gelinkt werden, und zwar unter Verwendung der originalen, von Commodore veröffentlichten amiga.lib. Da diese urheberrechtlich geschützt ist, wird vbcc mit einem Nachbau dieser ausgeliefert; leider fehlen hier ein paar Funktionen, u.a. auch AddICRVector() und RemICRVector().

SAS/C:
Da ich selber nie SAS/C gekauft/verwendet habe, kann ich nicht eine mögliche Fehlerquelle bestimmen.

gcc:
Bei installiertem NDK3.9 (plus entsprechender Inline-Dateien, die selber erstellt werden müssen) ist das Kompilieren möglich und damit auch das Erstellen einer ausführbaren Datei.

MaxonC++/HiSoftC++:
Da dieser Compiler kein "__saveds" anbietet, muss das große Datenmodel verwendet werden, weil ein GetBaseReg()-Aufruf das Prozessorregister A4 zerstört - und dies innerhalb eines Interrupt-Handlers schwerwiegende Folgen hat.

StormC V3:
Sollte funktionieren - habe ich aber nicht ausprobiert.

Im Jahre 2002 habe ich das Beispiel veröffentlicht und es wurde von Mitgliedern der betreffenden Mailing-Liste kompiliert und ausprobiert - sprich Digitizer angeschlossen und gestartet.
Keiner von Ihnen beklagte ein Fehlverhalten noch dass das Beispiel nicht kompilierbar wäre - auch wenn einige den Quellcode an ihre Compiler anpassen mussten.

Da ich nicht jeden im Gebrauch befindlichen Compiler installiert habe und somit nicht meine Quellcodes komplett Compiler unabhängig gestalten kann, sollte dennoch nur nach geringfügigen Modifikationen des Quellcode jeder Compiler imstande sein, diesen zu übersetzen - auch SAS/C.
Was bei Dir schief geht, kann ich mangels Aussagekraft Deiner Antwort nicht sagen.
Ich tippe auf einen Fehler innerhalb Deiner Compiler-Umgebung.

Zu Guter Letzt: Du solltest das Beispiel eigentlich nur studieren um die gezeigte Methode zu verstehen, um sie dann in Deinem eigenen Programm zu verwenden.


Gruß



Anbei, hier die vbcc-Dateien:

vbcc "inline/cia_protos.h":

#ifndef _VBCCINLINE_CIA_H
#define _VBCCINLINE_CIA_H

#ifndef EXEC_LIBRARIES_H
#include <exec/libraries.h>
#endif

#ifndef EXEC_INTERRUPTS_H
#include <exec/interrupts.h>
#endif

struct Interrupt * __AddICRVector(__reg("a6") struct Library * resource, __reg("d0") LONG iCRBit, __reg("a1") struct Interrupt * interrupt)="\tjsr\t-6(a6)";
#define AddICRVector(resource, iCRBit, interrupt) __AddICRVector((resource), (iCRBit), (interrupt))

VOID __RemICRVector(__reg("a6") struct Library * resource, __reg("d0") LONG iCRBit, __reg("a1") struct Interrupt * interrupt)="\tjsr\t-12(a6)";
#define RemICRVector(resource, iCRBit, interrupt) __RemICRVector((resource), (iCRBit), (interrupt))

WORD __AbleICR(__reg("a6") struct Library * resource, __reg("d0") LONG mask)="\tjsr\t-18(a6)";
#define AbleICR(resource, mask) __AbleICR((resource), (mask))

WORD __SetICR(__reg("a6") struct Library * resource, __reg("d0") LONG mask)="\tjsr\t-24(a6)";
#define SetICR(resource, mask) __SetICR((resource), (mask))

#endif /* _VBCCINLINE_CIA_H */


"proto/cia.h":

#ifndef _PROTO_CIA_H
#define _PROTO_CIA_H

#ifndef EXEC_LIBRARIES_H
#include <exec/libraries.h>
#endif

#if !defined(CLIB_CIA_PROTOS_H) && !defined(__GNUC__)
#include <clib/cia_protos.h>
#endif

#ifndef __NOLIBBASE__
extern struct Library *CiaBase;
#endif

#ifdef __GNUC__
#include <inline/cia.h>
#elif defined(__VBCC__)
#include <inline/cia_protos.h>
#else
#include <pragma/cia_lib.h>
#endif

#endif /* _PROTO_CIA_H */


[ - Antworten - Zitieren - Direktlink - ]

17.10.2007, 22:55 Uhr

MaikG
Posts: 5172
Nutzer
Okay, datei 1 hab ich erstellt, datei 2 an die existierende angefügt


12.Work4:vbcc> vc -c99 -cpu=68020 -O1 -sc -sd AudioGrabber.c -o AudioGrabber -lvcs -lamiga

error 1002 in line 45: invalid extension
vasmm68k_mot fehlgeschlagen Rückgabewert 20
vasmm68k_mot -quiet -Fhunk -phxass -opt-pea -opt-clr -opt-fconst "T:t_12_0.asm" -o "T:t_12_0.o" failed



vbcc gibt nicht wirklich was hilfreiches aus...


>Zu Guter Letzt: Du solltest das Beispiel eigentlich nur studieren
>um die gezeigte Methode zu verstehen, um sie dann in Deinem eigenen
>Programm zu verwenden.

Das ist nicht so einfach, will ich es auf Basic portieren tauchen
etliche neue fragen auf. Wenn ich es bei C belasse, kann ich es
so lassen wie es ist und als Inline Assembler einbinden.
Oder aber Starten und mittels eines Signals wieder beenden.

[ Dieser Beitrag wurde von MaikG am 17.10.2007 um 22:59 Uhr geändert. ]

[ - Antworten - Zitieren - Direktlink - ]

18.10.2007, 12:16 Uhr

Der_Wanderer
Posts: 1229
Nutzer
Also in amiblitz3 ginge das in etwa so:

code:
XINCLUDE "ciatimer.include.bb2"

*SampleData.b = $BFE101
Samplerate.l  = 16000
TPM.l         = Samplerate * 60 ; Ticks-Per-Minute
BufferSize.l  = 120000
BufferIndex.l = 0

Dim MyBuffer.b(BufferSize)

; setze unsere eigene Funktion in den Interrupt
timer_SetInterruptCode{?ciainterrupt,0}

; timer öffnen
If timer_Open{-1,-1,TPM}

  ; los geht's!
  timer_Start{} 

  ; wir warten bis der Puffer voll ist...
  While BufferIndex<BufferSize: Delay_ 1 : Wend

  ; stop!
  timer_Stop{}

  ; jetzt ist unser Array voll mit sample daten
  ; wir können es auch einfach speichern...
  fid.l = ffa_WriteHeader{"Ram:MySound.wav",@"WAVE",16000,8,1}
  If fid>=0
    ffa_WriteMem{fid,&MyBuffer(0),BufferSize}
    ffa_Close{fid}
  End If
End If
End

ciainterrupt:
!basica7
If (BufferIndex<BufferSize) {
  tmp=Peek.b(*SampleData);       // Register auslesen
  tmp-=0x80;            // unsigned => signed
  MyBuffer(BufferIndex) = tmp; // speichern
  BufferIndex+1;
End If
!asma7
RTS


Das geht aber nur mit den neuesten Inlcudes, da es SetInterruptCode
im aktuellen release noch nicht gibt, müsstest du per Hand
setzen.
Wenn dich das interessiert, kann ich dir das zuschicken.
Du siehst also, es ist nicht soooo kompliziert.
Ich hab das obige jetzt nicht compiliert, sondern nur hier on-the-fly geschrieben, aber es sollte gehen.

--
Thilo Köhler, Author von:
HD-Rec, Sweeper, Samplemanager, ArTKanoid, Monkeyscript, Toadies, AsteroidsTR, TuiTED, PosTED, TKPlayer, AudioConverter, ScreenCam, PerlinFX, MapEdit, TK AB3 Includes und viele mehr...
Homepage: http://www.hd-rec.de


[ Dieser Beitrag wurde von Der_Wanderer am 18.10.2007 um 13:36 Uhr geändert. ]

[ Dieser Beitrag wurde von Der_Wanderer am 18.10.2007 um 13:37 Uhr geändert. ]

[ - Antworten - Zitieren - Direktlink - ]

20.10.2007, 11:31 Uhr

jolo
Posts: 108
Nutzer
Hi,


Zitat:
Okay, datei 1 hab ich erstellt, datei 2 an die existierende angefügt

Falsch, beide ersetzen die jeweils existierende Datei.


Zitat:
error 1002 in line 45: invalid extension
vasmm68k_mot fehlgeschlagen Rückgabewert 20


Ich würde das mal so interpretieren, dass der Assembler (nicht der C-Compiler) auf eine ungültige Erweiterung stieß.
Der vom C-Compiler generierte Code kann so nicht vom Assmbler übersetzt werden.

Mögliche Ursachen:
Veralteter Compiler - benutzt Du die aktuellste Version des vbcc-Compilers?
Hast Du den Quellcode richtig kopiert - keine unerlaubten Charaktere im Quelltext?
Stack-Größe ausreichend? 40 KBytes dürften es schon sein...

Wie dem auch sei, unter http://www.amimedic.de auf den Link "Archive" klicken und Du kannst Dir den Quellcode und die zwei Include-Dateien im Original herunter laden.

Beim Kompilieren solltest Du die "amiga.lib" nicht mehr benötigen, so dass der Aufruf wie folgt aussieht:

vc -c99 -cpu=68020 -O1 -sc -sd AudioGrabber.c -o AudioGrabber -lvcs


Zitat:
Das ist nicht so einfach, will ich es auf Basic portieren tauchen
etliche neue fragen auf.


Da ich von Basic keine Ahnung habe, kann ich Dir hier nicht weiter helfen.

Zitat:
Wenn ich es bei C belasse, kann ich es so lassen wie es ist und als Inline Assembler einbinden.
Oder aber Starten und mittels eines Signals wieder beenden.


Meinens Erachtens nach keine wirklich gute Idee.
Wie viele Audiopuffer möchtest Du verwenden? Einen, zwei oder drei?
Wie übermittelst Du den Speicherbereich, welcher die Audiodaten aufnehmen soll?
Was macht Dein Programm in der Zeit, wo keine Arbeiten anfallen und nur der Interrupt aktiv ist?
Wie willst Du Deinem Programm signalisieren, dass der Puffer voll ist? Dazu braucht die Interrupt-Routine die Task-Adresse Deiner Anwendung und das Signal-Bit, welches Deine Anwendung aufwachen lässt.

Das Beispiel was ich hier zeigte, ist simpel. Es startet einen Interrupt der Audiodaten synchron zur Ausgabe über die Audiohardware liest. Das heißt, dass ein Byte vom parallelen Port im gleichen Zeitrahmen (Abtastrate) gelesen wird, mit dem sie ausgegeben wird (16688 Hz entsprechen 16688 Bytes pro Sekunde). Also liest der Interrupt 16688 Bytes (+/-) pro Sekunde vom parallelen Port.

Die zwei möglichen CIA-Timer (A und B) zählen von einem bestimmten Wert herunter. Wenn dieser null ist, wird der Interrupt-Code ausgeführt und der Wert für das entsprechende CIA-Register wieder hergestellt.
Da die CIAs ursprünglich vom E-Takt (synchrone Bussteuerung) des Prozessor getaktet wurden, werden sie nur mit einem Zehntel dieser normalen Taktfrequenz betrieben:
Taktfrequenz = 7,09379 MHz / 10 = 709379 Hz
Somit entspricht 1 Sekunde 709379 Ticks (Delay-Wert für den CIA-Timer).
Da die CIA-Timer aber nur 16-Bit Werte erlauben, können nur Werte bis zu einer zehntel Sekunde verwendet werden:
709379 / 65535 = ~1/10 Sekunde

Wie gesagt, um nun 16688 Bytes in der Sekunde zu lesen, muss man 0,0000599232982 Sekunden warten, bevor man das nächste Byte liest (1 / 16688 = 0,0000599232982).
Dies ist gleichbedeutend mit einem Wert für einen der beiden CIA-Timer von: 709379 / 16688 = 42,508329.
Da man mit absoluten Werten arbeiten muss, muss entweder auf- oder abgerundet werden.
Bei einem Wert von 42 wird demnach 16889 pro Sekunde ein Byte gelesen, bei einem Wert von 43 16497-mal (709379 / x = Bytes pro Sekunde).

Das ist der ganze Clou - und ich gehe davon aus, dass es aufwendiger ist, zwei Programmiersprachen zu kombinieren als das Ganze mit nur einer zu realisieren.

Allerdings habe ich meine Bedenken bei der Verwendung von Basic für Interrupts. Werden keine Prozessor-Register zerstört? Wird der Interrupt mit gesetztem Z-Flag beendet? Ist der Code kurz und optimiert genug um das System nicht zu beeinträchtigen?
Selbst einem C-Compiler muss man schon sagen, dass der generierte Code Interrupt konform sein soll, ansonsten geht's unter Umständen schief.

Gruß

[ - Antworten - Zitieren - Direktlink - ]

20.10.2007, 18:10 Uhr

MaikG
Posts: 5172
Nutzer
>Wie übermittelst Du den Speicherbereich, welcher die Audiodaten aufnehmen soll?

Eigentlich dachte ich mir das so, wenn der Sampler zum Audioausgang
geschaltet werden soll starte ich das Programm, wenn z.B. 5 sek
um sind stoppe ich es. Gut, wenn ich jetzt mit Puffern
arbeiten will muss ich da was übergeben. Aber man kann da
irgendwie Daten per Datenregister rüberschicken. Da gibt es
ein Beispiel für.

>Was macht Dein Programm in der Zeit, wo keine Arbeiten anfallen und nur der Interrupt aktiv ist?
>Wie willst Du Deinem Programm signalisieren, dass der Puffer voll ist? Dazu braucht die Interrupt-Routine die Task-Adresse Deiner Anwendung und das Signal-Bit, welches Deine Anwendung aufwachen lässt.

>Das Beispiel was ich hier zeigte, ist simpel.

Wenn man sich damit schon beschäftigt hat bestimmt...


>Werden keine Prozessor-Register zerstört?

Kein schimmer.

>Wird der Interrupt mit gesetztem Z-Flag beendet?

Weiss nichtmal was ein Z-Flag ist, aber MBasic wird wohl nicht
wissen was ein Interrupt ist und da nichts machen.

>Ist der Code kurz und optimiert genug um das System nicht zu
>beeinträchtigen?

Das Kopieren von einem Byte von Sampler->Buffer bekomme ich auch
in Assembler hin(dürften 4 Befehle sein). Nur vorher muss ich genau verstehen wie das mit
den CIA's Funktioniert. Z.b. Teilt man den die Adresse des Aufzurufenden
Programms im Speicher mit?

[ Dieser Beitrag wurde von MaikG am 20.10.2007 um 18:11 Uhr geändert. ]

[ - Antworten - Zitieren - Direktlink - ]

23.10.2007, 21:14 Uhr

jolo
Posts: 108
Nutzer
Hi!

Zitat:
Wie übermittelst Du den Speicherbereich, welcher die Audiodaten aufnehmen soll?
Zitat:
Eigentlich dachte ich mir das so, wenn der Sampler zum Audioausgang geschaltet werden soll starte ich das Programm, wenn z.B. 5 sek um sind stoppe ich es. Gut, wenn ich jetzt mit Puffern arbeiten will muss ich da was übergeben. Aber man kann da irgendwie Daten per Datenregister rüberschicken. Da gibt es ein Beispiel für.


Dein Interrupt-Code wird unabhängig von Deinem Hauptprogramm ausgeführt - somit ist der Datentransfer über Prozessor-Register ausgeschlossen. Du könnest aber über is_Data (Interrupt-Struktur) dem Interrupt-Code Daten bereitstellen.
Modifiziertes Beispiel habe ich hochgeladen: http://www.amimedic.de und dem Link "Archive" folgen.
Habe auch den Quellcode ins HTML-Format konvertiert, damit Du diesen besser lesen kannst, im Falle dass Du einen Texteditor ohne Text- Syntax-Highlighting für die Programmiersprache "C" verwendest.


Zitat:
Was macht Dein Programm in der Zeit, wo keine Arbeiten anfallen und nur der Interrupt aktiv ist?
Wie willst Du Deinem Programm signalisieren, dass der Puffer voll ist? Dazu braucht die Interrupt-Routine die Task-Adresse Deiner Anwendung und das Signal-Bit, welches Deine Anwendung aufwachen lässt.

Das Beispiel was ich hier zeigte, ist simpel.
Zitat:
Wenn man sich damit schon beschäftigt hat bestimmt...


Lasse mal alle programmiertechnischen Belange außer Acht. Das was dann noch übrig bleibt ist der Kern den Du in irgendeiner Weise zu implementieren hast:

1. In kontinuierlichen Zeitabständen ein Byte vom parallelen Port lesen.
2. Dieses Byte vorzeichengerecht irgendwo ablegen.
3. Den Vorgang wiederholen.

Ob Du nun einen Interrupt verwendest liegt ganz allein bei Dir.

Du kannst das auch innerhalb einer Schleife in Deinem Hauptprogramm machen.

Ich habe in dem betreffenden Beispiel einen Interrupt verwendet, damit die Ausgabe (Audiohardware) keinen großen Puffer benötigt und man das Ding (AudioGrabber) stundenlang dudeln lassen kann. Ich hätte auch einen Speicherbereich verwenden können, der nur 4 Bytes groß ist, da die Ausgabe analog zur Eingabe erfolgt. Da die Audiohardware gestartet wird sobald 2 Bytes gelesen wurden, braucht man nochmals 2 Bytes um diese vorhandenen Audio-Daten nicht zu überschreiben (die Audiohardware liest immer 2 Bytes auf einmal - sprich 16 Bits).

Nochmals: Die Puffergröße für die Audio-Daten die mittels Hardware hörbar gemacht werden, ist unerheblich. Alles im Bereich von 4 Bytes bis 128 KBytes kann verwendet werden.


Zitat:
Werden keine Prozessor-Register zerstört?
Zitat:
Kein schimmer.


Interrupts müssen bestimmte Voraussetzungen erfüllen damit es nicht zum Systemabsturz oder Verlangsamung des Systems kommt. Ich gehe davon aus, dass das in Basic nicht so ohne weiteres bewerkstelligt werden kann.


Zitat:
Wird der Interrupt mit gesetztem Z-Flag beendet?
Zitat:
Weiss nichtmal was ein Z-Flag ist, aber MBasic wird wohl nicht wissen was ein Interrupt ist und da nichts machen.


Das Z-Flag ist ein Bit im Prozessor-Statusregister das immer dann gesetzt wird, wenn eine CPU-Instruktion das Ergebnis null lieferte: Vergleichsoperationen, Subtraktion, Addition usw.


Zitat:
Ist der Code kurz und optimiert genug um das System nicht zu beeinträchtigen?

Zitat:
Das Kopieren von einem Byte von Sampler->Buffer bekomme ich auch in Assembler hin(dürften 4 Befehle sein). Nur vorher muss ich genau verstehen wie das mit den CIA's Funktioniert. Z.b. Teilt man den die Adresse des Aufzurufenden Programms im Speicher mit?


Die CIAs sind strikt gesagt keine Interrupts - es sind Hardwarebausteine die beim Erreichen eines bestimmten Zustandes Prozessor-Interrupts auslösen können.
Der Prozessor verzweigt dann in eine Systemroutine, in welcher vermerkt ist, wo der auszuführende Code zu finden ist. Um nun dieser Systemroutine mitzuteilen, wo Dein Code zu finden ist, musst Du vorab eine Interrupt-Struktur initialisieren.

Schritt 1)
Da wir einen CIA-B-Timer verwenden wollen, müssen wir erst einmal Zugriff auf die CIA-Resource bekommen, also CIA-Resource öffnen.

Schritt 2)
Speicher für die Interrupt-Struktur anfordern.

Schritt 3)
Interrupt-Struktur initialisieren; evt. in is_Data eigene Struktur eintragen, dann kann der Interrupt-Code davon Gebrauch machen (wird im Prozessor-Register A1 übergeben).

Schritt 4)
Einen der beiden Abwärts-Timer anfordern (AddICRVector).

Schritt 5)
Je nach dem welcher Timer noch frei ist, diesen mit den korrekten Werten initialisieren.

Der Interrupt läuft jetzt.

is_Code zeigt auf den Code, der ausgeführt werden soll.
is_Data evt. auf Deine eigene Struktur, auf die sowohl Dein Hauptprogramm als auch der Interrupt-Code zugreifen können (Interrupt-Struktur -> is_Data -> Interrupt-Code -> Prozessor-Register A1).
Du kannst aber auch globale Variablen dafür verwenden.

Da ein Level-6 Interrupt (CIA-B) keinen Level-4 Interrupt (Audiohardware) aufrufen darf, habe ich im Beispiel einen Level-1 (Cause/Software-Interrupt) benutzt, um die Audio-Ausgabe einmalig zu starten.
Ich hoffe, Du verstehst den Source-Code jetzt besser.


Anbei, das neue Bespiel sollte Dir zeigen, wie man Daten des Hauptprogramms dem Interrupt-Code zugänglich macht (is_Data wird verwendet).


Gruß

[ - Antworten - Zitieren - Direktlink - ]

23.10.2007, 23:27 Uhr

MaikG
Posts: 5172
Nutzer
>1. In kontinuierlichen Zeitabständen ein Byte vom parallelen Port lesen.
>2. Dieses Byte vorzeichengerecht irgendwo ablegen.
>3. Den Vorgang wiederholen.

Ja, wobei 1./ Zeitabstände das Problem sind

>Ob Du nun einen Interrupt verwendest liegt ganz allein bei Dir.


Also die CIAs zählen runter und lösen einen Interrupt aus.
Könnte ich diesen Interrupt auch per Hardwareregister lesen
und zu 0 setzten und dann geht es von vorne los?
Ich meine das währe so bei 100% CPU last aber es währe erstmal ein
Timing.

>Interrupts müssen bestimmte Voraussetzungen erfüllen damit es nicht
>zum Systemabsturz oder Verlangsamung des Systems kommt. Ich gehe
>davon aus, dass das in Basic nicht so ohne weiteres bewerkstelligt
>werden kann.

Also a0, a1, d2 und d3 dürfen in jedem fall von einem Inline
ASM Code verwendet werden.

Ja, also die beiden Dateien hab ich jetzt 1:1 ersetzt die
fehlermeldung ändert sich nicht. Beim 2. Source ist es nur
eine andere Zeilennummer.

[ - Antworten - Zitieren - Direktlink - ]

24.10.2007, 20:11 Uhr

jolo
Posts: 108
Nutzer
Hi!

Zitat:
1. In kontinuierlichen Zeitabständen ein Byte vom parallelen Port lesen.
2. Dieses Byte vorzeichengerecht irgendwo ablegen.
3. Den Vorgang wiederholen.

Zitat:
Ja, wobei 1./ Zeitabstände das Problem sind


Bestimmt nicht. Du musst wie jeder andere auch Dir das Wissen dazu aneignen.
Entweder Du benutzt einen Interrupt oder eine Schleife in Deinem Hauptprogramm, die mittels des Timer-Device eine bestimmte Zeit wartet.
In beiden Fällen musst Du die entsprechende Literatur lesen.


Zitat:
Ob Du nun einen Interrupt verwendest liegt ganz allein bei Dir.

Zitat:
Also die CIAs zählen runter und lösen einen Interrupt aus.
Könnte ich diesen Interrupt auch per Hardwareregister lesen und zu 0 setzten und dann geht es von vorne los?



Du verstehst den Begriff Interrupt nicht richtig. Es ist eine wiederkehrende Unterbrechung. Interrupts laufen mit höheren Prioritäten als Prozesse, sprich, bevor ein Prozess wieder Rechenzeit bekommt, sind alle (anstehenden) Interrupts schon abgearbeitet worden (meistens werkeln 15 Interrupts im Hintergrund die Du nicht bemerkst).
Interrupts unterbrechen auch Prozesse zu jeder X-beliebigen Zeit, da ja nur ein Prozessor im System vorhanden ist.

Ein Beispiel:
Der Prozessor befindet sich mitten in Deinem Programm (irgendeine Schleife wird abgearbeitet)- jetzt löst ein Hardwarebaustein einen Prozessor-Interrupt aus. Der Prozessor unterbricht sofort seine derzeitige Tätigkeit und springt in die Systemroutine ein, die für diesen Interrupt zuständig ist. Diese Routine wiederum springt die Routine an, die Du mittels der Interrupt-Struktur spezifiziert hast. Nachdem Deine Interrupt-Routine abgearbeitet worden ist, springt der Prozessor wieder zurück zu Deinem Hauptprogramm - genau an die Stelle, wo er es verlassen hat. Während Du glaubst, dass Deine Schleife ununterbrochen abgearbeitet wird, wird sie in Wirklichkeit zig tausendmal unterbrochen - Du merkst das aber nicht - und sollst es auch nicht!
Jedes Betriebssystem benutzt Interrupts um irgendwelche Dinge (Werte oder was auch immer) in bestimmten Zeitabständen abzufragen. Du, als Anwender und Anwendungsprogrammierer, merkst davon rein gar nichts - und sollst es ja auch schließlich nicht. Für Dich existiert nur Dein Programm. Alles andere ist Sache des Betriebssystems.

Zurück zu unserem Beispiel:
Die beiden verwendeten CIA-B Timer zählen von einem bestimmten Wert auf null herunter - wenn der Wert null erreicht worden ist, wird ein Prozessor-Interrupt ausgelöst und gleichzeitig der originale, von Dir gewählte Wert wieder hergestellt. In unserem Beispiel 42. Du brauchst diesen Wert nicht wieder herstellen - das erledigt die Hardware! Beim erneuten Erreichen des Wertes null wird also wieder ein Prozessor-Interrupt generiert - und zwar solange, bis Du dies verbietest (RemICRVector).
Somit werden bei einer Laufzeit von 5 Sekunden 83440 (16688 * 5) Interrupts generiert.

Jeder Wert-Wechsel dauert exakt 0,00000140968368 Sekunden - oder anders ausgedrückt: 1/709379 Sekunde. Du erinnerst Dich - die CIAs werden mit einem Zehntel des ursprünglichen CPU-Taktes getaktet (damals waren 7,09 MHz Stand der Dinge).
Das heißt, bei jedem Wechsel des Wertes, 42 zu 41, 41 zu 40, 40 zu 39 usw., vergehen jeweils 0,00000140968368 Sekunden. Multipliziert man 0,00000140968368 mit 42 so erhält man 0,00005920671456 Sekunden - somit werden alle 0,00005920671456 Sekunden Interrupts generiert. Multiplizierst Du 0,00005920671456 Sekunden mit 16688 so erhältst Du (circa) 1 Sekunde - was wiederum bedeutet, dass pro Sekunde 16688-mal (+/-) ein Interrupt generiert wird.


Zitat:
Könnte ich diesen Interrupt auch per Hardwareregister lesen und zu 0 setzten und dann geht es von vorne los?

Beim Initialisieren der Hardwareregister wird der Wert 42 in die entsprechenden Register der CIAs einmalig "gepoked".

ciab->ciatXlo = (Timeout[PowerIndex] & 0xFF)
ciab->ciatXhi = (Timeout[PowerIndex] >> 8 & 0xFF)

Timeout[0] (für 50 Hz Netzfrequenz) = 42
Timeout[1] (für 60 Hz Netzfrequenz) = 43
PowerIndex ist entweder 0 oder 1 - je nach dem wo die Maschine betrieben wird (Japan & Amerika = 60 Hz, Deutschland = 50 Hz). Upps, in Japan gibt es sowohl als auch...

Und wie oben beschrieben, hast Du es gar nicht nötig, diesen Wert wieder herzustellen. Das macht die Hardware.

Es wäre hilfreich, wenn Du ein klein wenig "C" verstehen würdest.
Ich weiß nicht wie gut Deine Assembler-Kenntnisse sind; wenn sie ausreichend sind, kann ich die entsprechenden Fragmente auch als Assembler-Quellcode posten; das Original wurde eh in 68k-Assembler verfasst.


Zitat:
Interrupts müssen bestimmte Voraussetzungen erfüllen damit es nicht zum Systemabsturz oder Verlangsamung des Systems kommt. Ich gehe davon aus, dass das in Basic nicht so ohne weiteres bewerkstelligt werden kann.

Zitat:
Also a0, a1, d2 und d3 dürfen in jedem fall von einem Inline ASM Code verwendet werden.


Wie gut sind Deine Assembler-Kenntnisse? - Siehe oben.

Selbst bei der Verwendung von anderen Prozessor-Registern würde Dein Basic-Interpreter oder auch das kompilierte Programm nicht abstürzen, weil der Interrupt-Code rein gar nichts mit Deinem Basic-Programm zu tun hat. Sieh den Interrupt-Code als zweites, eigenständiges Programm, das simultan zu Deinem Hauptprogramm läuft.
Dementsprechend ist es auch etwas schwieriger, Daten zwischen diesen beiden eigenständigen "Programmen" auszutauschen.
Da C-Compiler näher an der Hardware operieren als ein Basic-Interpreter (Basic kompiliertes Programm), bieten diese Mechanismen an, die es dem Programmier erleichtern, solche Dinge wie Interrupts zu handhaben.


Zitat:
Ja, also die beiden Dateien hab ich jetzt 1:1 ersetzt die fehlermeldung ändert sich nicht. Beim 2. Source ist es nur eine andere Zeilennummer.

Es gibt zwei Möglichkeiten.
Entweder habe ich Compiler-Umgebungen die kein anderer hat oder Du hast eine fehlerhafte.
Unter gcc, vbcc und MaxonC++ sind diese Beispiele in meinen Umgebungen kompilierbar. Es wäre hilfreich, wenn irgendjemand hier aus dem Forum das eine oder andere bestätigen könnte.


Gruß

[ - Antworten - Zitieren - Direktlink - ]

24.10.2007, 22:56 Uhr

MaikG
Posts: 5172
Nutzer
>Entweder Du benutzt einen Interrupt oder eine Schleife in Deinem
>Hauptprogramm, die mittels des Timer-Device eine bestimmte Zeit
>wartet.

Wie das theoretisch mit dem Timer-Device funktionieren sollte
weiss ich selbstverständlich. Nur geht dieses nicht für 16000 HZ,
nur bis 12000 HZ geht das sauber. Das auch nur unter einem 060er.


>Du verstehst den Begriff Interrupt nicht richtig.

Doch, doch. Nur ist es vermutlich einfacher für mich diesen
CIA-Countdown zu starten und dann auf eine 0 im entsprechenden
Register zu warten.


>Es wäre hilfreich, wenn Du ein klein wenig "C" verstehen würdest.

Ich kann schon etwas C, nur hab ich noch nie mit Interrupts gearbeitet.


>Ich weiß nicht wie gut Deine Assembler-Kenntnisse sind; wenn sie
>ausreichend sind, kann ich die entsprechenden Fragmente auch als
>Assembler-Quellcode posten; das Original wurde eh in 68k-Assembler
>verfasst.

Mittlerweile kann ich C besser als Assembler.
Obwohl in ASM könnte man es sicherlich "Compilieren".


>Sieh den Interrupt-Code als zweites, eigenständiges Programm, das
>simultan zu Deinem Hauptprogramm läuft.

Also müsste ich alles ausser "SUPERIOR" in MBasic übersetzten
und SUPERIOR währe dann der Interrupt welchen ich per ASM
einbinden könnte.


>Es gibt zwei Möglichkeiten.
>Entweder habe ich Compiler-Umgebungen die kein anderer hat oder Du hast eine fehlerhafte.

Ich habe schon erfolgreich andere progs damit Compiliert.

>Unter gcc, vbcc und MaxonC++ sind diese Beispiele in meinen Umgebungen kompilierbar.

Muss mal gcc probieren aber bei den war die Installation so kompliziert
das ich mir bei dem tatsächlich nicht sicher bin ob der geht.

[ - Antworten - Zitieren - Direktlink - ]

25.10.2007, 22:21 Uhr

whose
Posts: 2156
Nutzer
@jolo:

Also, ich hab das 1. AudioGrabber gerade mal durch den vbcc gescheucht (Cubic-Installation), Compilerlauf war völlig problemlos (keine Warnungen oder Fehler). Groß auf Funktion testen konnte ich es mangels Sampler allerdings nicht ;-)

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

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

Grüße

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?

--
---

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


[ Dieser Beitrag wurde von whose am 25.10.2007 um 23:14 Uhr geändert. ]

[ - Antworten - Zitieren - Direktlink - ]

26.10.2007, 20:16 Uhr

jolo
Posts: 108
Nutzer
@MaikG:

Hi.

Zitat:
Entweder Du benutzt einen Interrupt oder eine Schleife in Deinem Hauptprogramm, die mittels des Timer-Device eine bestimmte Zeit wartet.

Zitat:
Wie das theoretisch mit dem Timer-Device funktionieren sollte weiss ich selbstverständlich. Nur geht dieses nicht für 16000 HZ, nur bis 12000 HZ geht das sauber. Das auch nur unter einem 060er.


Ohne abgeschaltetes Multitasking wirst Du generell Schwierigkeiten haben, Deinen Prozess (nicht Interrupt!) in solch kurzen Intervallen eine Aktion ausführen zu lassen. Erstes macht Dir jeder Interrupt einen Strich durch die Rechnung, zweitens jeder Prozess, drittens der Task-Scheduler, der u.a. berechnet, wie viel Zeit Deinem Prozess noch zur Verfügung steht. Je mehr Dein Prozess an Rechenzeit verbraucht, desto niedriger die Priorität, die er vom Task-Scheduler zugewiesen bekommt.
Du kannst das zwar durch das Hochsetzen der Task-Priorität bis zu einem gewissen Maß kompensieren, allerdings nicht vollständig.
Auf einem 68000er System, in dem die CPU mit 7 MHz getaktet wird (A1000/A500/A2000/A600), hast Du nicht die geringste Chance ohne abgeschaltete Interrupts und Multitasking solch eine Leistung zu erzielen.
Deshalb war der Bildschirm während des Samplens damals immer schwarz - bis einer auf die Idee kam, entsprechend dem Signal die Hintergrundfarbe zu ändern. Erst da wurde es bunt...


Zitat:
Du verstehst den Begriff Interrupt nicht richtig.

Zitat:
Doch, doch. Nur ist es vermutlich einfacher für mich diesen CIA-Countdown zu starten und dann auf eine 0 im entsprechenden Register zu warten.


Nix da mit warten via Polling! :)
Wenn ein Prozess 16000-mal pro Sekunde Rechenzeit vom Betriebssystem verlangt, wird er ganz schnell ganz langsam. :)
Den Grund habe ich oben genannt - der Scheduler lässt dies gar nicht zu.

Deshalb wird im 2. Beispiel (AudioGrabber2) nur dann der Prozess via Signal-Bit benachrichtigt, wenn ein Puffer voll ist; Interrupts werden vom Task-Scheduler ignoriert.
Da beide verwendeten Puffer in diesem Beispiel für 2 Sekunden Sampling ausreichend sind, bekommt der Prozess alle 2 Sekunden eine Nachricht die besagt, dass einer der beiden Puffer voll ist - also 32 KBytes an Daten empfangen wurden.

Nochmals, der Interrupt läuft ständig - und füllt zwei Puffer, je 32 KBytes groß. Falls einer der Puffer voll ist, wird ein Signal an das Hauptprogramm gesendet; nun kann das Hauptprogramm diese Daten auswerten (oder kopieren), bis es wieder ein Signal vom Interrupt erhält, dass der andere Puffer auch voll ist, was zudem bedeutet, dass der Inhalt des ersten Puffers von jetzt an überschrieben wird. Beim nächsten Eintreffen des Signals wird der Pufferinhalt des zweiten Puffers überschrieben - und so fortlaufend, bis Du den Interrupt beendest.
Analog zum Füllen der beiden Puffer, wird ein Puffer zur Audioausgabe verwendet - also drei Puffer:

code:
SUPERIOR ULONG CIAB_Code( REG(a1, struct InterruptData *irq_data) )
{
	char data_byte;
	LONG i;
	UBYTE *buffer;

	i = 2;

	--> Lese Byte und setze Vorzeichen
	data_byte = ciaa->ciaprb;
	data_byte += 128;

	--> Audio-Puffer: Falls Daten darin noch Platz finden, Daten-Byte ablegen.
	if (AudioDataOffset < AUDIOSIZE)
	{
		AudioBuffer[AudioDataOffset] = data_byte;
		AudioDataOffset++;
	}
	else	--> Ansonsten, fange wieder bei null an.
	{
		AudioDataOffset = 0;
		AudioBuffer[AudioDataOffset] = data_byte;
		AudioDataOffset++;
	}

	--> Anbei: Die Audiohardware spielt ununterbrochen von diesem Puffer!

	--> Nur falls wir noch nicht die Audio-Ausgabe anwarfen, machen wir das
	--> jetzt - und zwar einmalig - die Audiohardware kümmert sich um den Rest.
	if (AudioDataOffset == STARTOFFSET)
	{
		if ( !AudioHit)
			Cause( Soft_IRQ);	--> Starte Audiohardware.
	}

	--> So, jetzt geht's mit den beiden anderen Puffern los!

	--> Passt das Daten-Byte noch in den aktuell verwendeten Puffer?
	--> ( singleBufSize = 32768 )
	--> ( dataIndex - irgendwo zwischen 0 und 32767 )
	if (irq_data->dataIndex < irq_data->singleBufSize)
	{
		--> Yep, passt.
		--> Welchen Puffer verwenden wir gerade: den ersten oder zweiten?
		--> ( bufIndex ist entweder 0 oder 1 (für zwei Puffer) und somit
		--> zeigt bufPtr[0] auf den ersten und bufPtr[1] auf den zweiten )
		buffer = irq_data->bufPtr[irq_data->bufIndex];

		--> Jetzt müssen wir die aktuelle Puffer-Position (0 bis 32767)
		--> noch auf die Adresse addieren, und wir wissen wo das Daten-Byte
		--> eingeschoben werden soll.
		buffer += irq_data->dataIndex;

		--> Schreibe das Daten-Byte in den Puffer
		*buffer = (UBYTE) data_byte;

		--> Position (irgendwo zwischen 0 - 32767) plus 1
		irq_data->dataIndex = irq_data->dataIndex + 1;
	}
	else
	{
		--> Nö, Daten-Byte passt nicht mehr (Position (dataIndex) = 32768)

		--> In der übermittelten Struktur setzen wir jetzt "filledBuffer"
		--> auf die Adresse des vollen Puffers. Diese Variable kann dann
		--> das Hauptprogramm auslesen.
		irq_data->filledBuffer = irq_data->bufPtr[irq_data->bufIndex];

		--> Jetzt testen, ob wir noch einen weiteren Puffer zur Verfügung haben
		--> oder ob wir einen bestehenden Pufferinhalt überschreiben müssen.
		--> ( amountBuffer hat den Wert zwei - für 2 Puffer - und bufIndex ist
		--> entweder 0 oder 1 )
		if ( (irq_data->amountBuffers - 1) > irq_data->bufIndex)
			irq_data->bufIndex = irq_data->bufIndex + 1;	--> Nächster Puffer
									--> (bei nur zwei
									--> Puffern, immer
									--> der zweite)
		else
			irq_data->bufIndex = 0;		--> Ersten Puffer wieder verwenden

		--> Welchen Puffer verwenden wir gerade: den ersten oder zweiten?
		--> Anbei, Du könntest auch vier Puffer verwendet, diese Routine verkraftet
		--> das...
		buffer = irq_data->bufPtr[irq_data->bufIndex];

		--> Da wir sowieso mit dem ersten Byte im Puffer (Position == 0) beginnen,
		--> können wir uns das Errechnen der Position sparen.
		*buffer = (UBYTE) data_byte;

		--> Nächste Position, also dort wo das nächste Daten-Byte abgelegt wird,
		--> ist Position 1, da wir gerade Position 0 mit dem Daten-Byte belegt
		--> haben.
		irq_data->dataIndex = 1;

		--> Signalisiere Hauptprogramm, das ein Puffer voll ist.
		Signal( irq_data->mainTask, (1 << irq_data->sigBit));
	}


So, bleibt nur noch die Frage, was das Hauptprogramm zwischenzeitlich macht.
Es macht nicht viel:

code:
while (alive == TRUE)
{
	received = Wait( SIGBREAKF_CTRL_C|(1 << signalBit));
	if (received & SIGBREAKF_CTRL_C)
		alive = FALSE;
	else
		VFPrintf( Output(),
			  "Buffer at address 0x%08lx full!\n",
			  (CONST APTR) &(IRQData->filledBuffer) );
}


Es wartet auf Signale ( Wait(...) ). Solange kein CTRL-C Signal eintrifft, bleibt "alive" wahr - und dieser Code-Block wird nicht verlassen.
Falls nun das Signal vom Interrupt eintrifft, wird nur eine Meldung ausgegeben, dass Puffer X voll ist, also einer der beiden Puffer seine Kapazität erreicht hat.


Damit Du siehst, dass beide Puffer kontinuierlich angesprochen werden, gibt VFPrintF() die Adresse des vollen Puffers aus.
Starte mal das Beispiel - dann wird Dir einiges klarer.

Zitat:
Es wäre hilfreich, wenn Du ein klein wenig "C" verstehen würdest.

Zitat:
Ich kann schon etwas C, nur hab ich noch nie mit Interrupts gearbeitet.


Hmmm, aber irgendwo muss der Hase doch begraben sein? Das Beispiel ist wirklich primitiv.
Es würde mir helfen, wenn Du mir sagen könntest, was Du nicht verstehst - ansonsten könnte dies eine niemals endende Geschichte werden - und für alle Parteien frustrierend.

Es würde mich auch freuen, wenn andere hier im Forum Hilfe leisten würden, da ich nicht allwissend bin und manchmal Schwierigkeiten habe, Gedankengänge anderer zu folgen, weil für mich alles schlüssig ist.
Ich bin kein Dozent - dementsprechend tue ich mich auch schwer, etwas was mir selbstverständlich erscheint, anderen mitzuteilen.


Zitat:
Ich weiß nicht wie gut Deine Assembler-Kenntnisse sind; wenn sie ausreichend sind, kann ich die entsprechenden Fragmente auch als Assembler-Quellcode posten; das Original wurde eh in 68k-Assembler verfasst.

Zitat:
Mittlerweile kann ich C besser als Assembler.
Obwohl in ASM könnte man es sicherlich "Compilieren".


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


Zitat:
Sieh den Interrupt-Code als zweites, eigenständiges Programm, das simultan zu Deinem Hauptprogramm läuft.

Zitat:
Also müsste ich alles ausser "SUPERIOR" in MBasic übersetzten und SUPERIOR währe dann der Interrupt welchen ich per ASM einbinden könnte.


Ja, denn SUPERIOR besagt nichts anderes, als dass der Code Interrupt tauglich sein soll und zusätzlich das Prozessor-Register A4 mit der Adresse des Datenbereichs geladen werden muss (kleines Datenmodell).


Zitat:
Es gibt zwei Möglichkeiten.
Entweder habe ich Compiler-Umgebungen die kein anderer hat oder Du hast eine fehlerhafte.

Zitat:
Ich habe schon erfolgreich andere progs damit Compiliert.


Aktuelle Version von vbcc?


Zitat:
Unter gcc, vbcc und MaxonC++ sind diese Beispiele in meinen Umgebungen kompilierbar.

Zitat:
Muss mal gcc probieren aber bei den war die Installation so kompliziert
das ich mir bei dem tatsächlich nicht sicher bin ob der geht.



vbcc kommt mit einem Installer-Skript - und ist damit wesentlich einfacher zu installieren als gcc.
Allerdings habe ich aber auch nur ca. 1 Stunde für die Installation von gcc gebraucht.
Dafür aber ca. 3 Stunden um alle aktuellen Dateien rauszusuchen und zu saugen...


Anbei, für Zitate verwendet man [quote] und [/quote]. Steht zwar nirgendwo beschrieben, funktioniert aber.


Gruß

[ - 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-2022 by amiga-news.de - alle Rechte vorbehalten.
.