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

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

1 2 3 -4- Ergebnisse der Suche: 110 Treffer (30 pro Seite)
jolo   Nutzer

03.11.2007, 12:07 Uhr

[ - Direktlink - ]
Thema: Registerzugriff in C
Brett: Programmierung

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)?
 
jolo   Nutzer

02.11.2007, 17:56 Uhr

[ - Direktlink - ]
Thema: Registerzugriff in C
Brett: Programmierung

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ß
 
jolo   Nutzer

01.11.2007, 12:28 Uhr

[ - Direktlink - ]
Thema: Registerzugriff in C
Brett: Programmierung

@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ß
 
jolo   Nutzer

01.11.2007, 12:24 Uhr

[ - Direktlink - ]
Thema: Registerzugriff in C
Brett: Programmierung

@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ß
 
jolo   Nutzer

30.10.2007, 23:48 Uhr

[ - Direktlink - ]
Thema: Registerzugriff in C
Brett: Programmierung

@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ß
 
jolo   Nutzer

30.10.2007, 23:46 Uhr

[ - Direktlink - ]
Thema: Registerzugriff in C
Brett: Programmierung

@MaikG:

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

Gruß
 
jolo   Nutzer

27.10.2007, 19:33 Uhr

[ - Direktlink - ]
Thema: Registerzugriff in C
Brett: Programmierung

@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 :)
 
jolo   Nutzer

27.10.2007, 18:52 Uhr

[ - Direktlink - ]
Thema: Registerzugriff in C
Brett: Programmierung

@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ß
 
jolo   Nutzer

26.10.2007, 20:35 Uhr

[ - Direktlink - ]
Thema: Registerzugriff in C
Brett: Programmierung

@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ß
 
jolo   Nutzer

26.10.2007, 20:16 Uhr

[ - Direktlink - ]
Thema: Registerzugriff in C
Brett: Programmierung

@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
Zitat:
und
. Steht zwar nirgendwo beschrieben, funktioniert aber.


Gruß
 
jolo   Nutzer

24.10.2007, 20:11 Uhr

[ - Direktlink - ]
Thema: Registerzugriff in C
Brett: Programmierung

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ß
 
jolo   Nutzer

23.10.2007, 21:14 Uhr

[ - Direktlink - ]
Thema: Registerzugriff in C
Brett: Programmierung

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ß
 
jolo   Nutzer

20.10.2007, 11:31 Uhr

[ - Direktlink - ]
Thema: Registerzugriff in C
Brett: Programmierung

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ß
 
jolo   Nutzer

17.10.2007, 20:55 Uhr

[ - Direktlink - ]
Thema: Registerzugriff in C
Brett: Programmierung

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)="tjsrt-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)="tjsrt-12(a6)";
#define RemICRVector(resource, iCRBit, interrupt) __RemICRVector((resource), (iCRBit), (interrupt))

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

WORD __SetICR(__reg("a6") struct Library * resource, __reg("d0") LONG mask)="tjsrt-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 */

 
jolo   Nutzer

15.10.2007, 21:59 Uhr

[ - Direktlink - ]
Thema: Registerzugriff in C
Brett: Programmierung

*** 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ß
 
jolo   Nutzer

02.09.2006, 14:41 Uhr

[ - Direktlink - ]
Thema: Screennotify & closewindow problem
Brett: Programmierung

Wenn die Fenster-Liste des Bildschirms leer ist und ScreenNotify trotzdem meldet, dass sich ein geöffnetes Fenster auf dem Bildschirm befindet, kann es sein, dass ScreenNotify bzw. CloseWorkBench() zusätzlich irgendeine Prefs-Datei überwachen – und diese noch nicht aktualisiert wurde?

Hab’ leider keine Ahnung von der ScreenNotify-Bibliothek – daher meine Frage…
 
jolo   Nutzer

02.09.2006, 10:35 Uhr

[ - Direktlink - ]
Thema: Screennotify & closewindow problem
Brett: Programmierung

Es kann bis zu einem Intuition-TimerTick dauern, bis ein Fenster wirklich geschlossen wird.

Um sicherzustellen, dass ein Fenster vom Bildschirm entfernt wurde, hilft eigentlich nur, die Fenster-Liste eines Bildschirms dahingegen zu untersuchen, ob dieses Fenster noch in der Fenster-Liste des Bildschirms vermerkt ist.
Allerdings kann dass nur durch Polling erreicht werden - und das widerspricht dem Grundgedanken des MultiTasking. Zudem kann niemand garantieren, dass nicht gleichzeitig ein neues Fenster mit dem alten Fenster-Wert (struct Window *) erstellt wurde, obwohl dies eher theoretischer Natur sein dürfte, also in der Praxis kaum eintreten wird.

Gruß
 
jolo   Nutzer

30.08.2006, 20:21 Uhr

[ - Direktlink - ]
Thema: Bitmaps nur durch 16 teilbare Breite?
Brett: Programmierung

Hi,

meines Wissens nach müssen alle Bitmaps mindestens eine durch sechzehn teilbare Größe besitzen; der Grund liegt im Blitter der originalen Chip-Sets des Amigas - dieser kann nur Speicherbreiche WORD-weise ansprechen. Um nun Punktweise Verschiebungen vorzunehmen, kommt der Barrel-Shifter ins Spiel (ist ein Teil des Blitter). Dieser "sagt" dem Blitter ab welchem WORD er blitten soll und welche Masken verwandt werden müssen. Da die Masken auch nur WORD-weise vom Blitter angewandt werden können und zudem noch WORD-Größe besitzen, muss eine Bitmap immer ein Vielfaches von sechzehn breit sein, ansonsten droht das Überschreiben von fremden Speicherbereichen.

Anbei, um unter AGA den Burst-Fetch Modus nutzen zu können, muss anstelle von 16-Bit Boundaries eine 64-Bit-Bereichsgrenze verwandt werden.

Da Linien und auch Texte mit Hilfe des Blitter auf dem Monitor gezaubert werden/wurden, müssen/mussten Bitmaps immer auf ein vielfaches von sechzehn aufgerundet werden.

Selbst unter Picasso96 war die Bitmap eines Bildschirms in der Auflösung von 800 x 600 vor ein paar Jahren noch mindestens 832 Punkte breit - ob sich das geändert hat, weiß ich nicht.


Gruß
 
jolo   Nutzer

04.04.2006, 23:35 Uhr

[ - Direktlink - ]
Thema: Hilfe beim 68k disassemblen
Brett: Programmierung

Hi.

Wie breit sind bei Dir DWORDs? 16 Bits oder 32?

Im Speicher wird ein Segment (Hunk) bis einschließlich OS 3.9 immer als Folge von:

#-8 Größe Segment (INT) (Größe inklusive dieser Struktur)
#-4 Zeiger (BPTR) auf's nächste Segment
#0 Segmentstart (Code, Data, BSS)

abgelegt.

Mir ist nicht bekannt bzw. ich kann mich nicht erinnern, dass es da noch ein drittes Langwort gäbe, das von irgendeiner Bedeutung wäre.


Gruß.
 
jolo   Nutzer

07.03.2006, 22:11 Uhr

[ - Direktlink - ]
Thema: Cursorpositionierung in C
Brett: Programmierung

@Rudi:

Das geht beim Amiga mittels CSI-Kodes (Command Sequence Introducer) - sollten in jedem guten AmigaDOS-Programmierhandbuch zu finden sein.

Gruß
 
 
1 2 3 -4- Ergebnisse der Suche: 110 Treffer (30 pro Seite)

Suchbegriffe
Schlüsselwörter      Benutzername
Suchoptionen
Nur in diesen Foren suchen
   nur ganze Wörter
Nur Titel anzeigen
alle Treffer anzeigen

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