Schneider / Amstrad CPC Forum

Amstrad / Schneider CPC => Programmierung => Topic started by: oobdoo on 15. February 2015, 14:25:19

Title: Wie programmiert man einen Interrupt in Z80
Post by: oobdoo on 15. February 2015, 14:25:19
Ich bin auf der Suche nach einer Möglichkeit, einen Interrupt zu programmieren, steige aber bei der Firmware nicht durch.  :(
Wie stellt man es da, das eine Aktion einmal oder öfter pro Sekunde gestartet wird?
Title: Re: Wie programmiert man einen Interrupt in Z80
Post by: almasys on 15. February 2015, 19:08:24
Hier hilft z.B. das Schneider CPC Systembuch: http://k1.spdns.de/Vintage/Schneider%20CPC/Das%20Schneider%20CPC%20Systembuch/z138.htm (http://k1.spdns.de/Vintage/Schneider%20CPC/Das%20Schneider%20CPC%20Systembuch/z138.htm)

Ansonsten gab es auch im Schneider Magazin bzw. Computer Partner eine gute Serie dazu, müsste ich mal raussuchen.

Viel Erfolg,
AMSi
Title: Re: Wie programmiert man einen Interrupt in Z80
Post by: TFM on 15. February 2015, 19:35:18
Du könntest natürlich auch einfach den Interrupt an Adresse &0038 abfangen und in dein Programm umleiten. In dem Fall gibt es sechs Interrupts pro Bild (also pro 1/50 Sekunde). Und jedes 6te mal kannst Du testen ob der Elektronenstrahl zurück läuft, um den Bildaufbau zu synchronisieren.

Das funktioniert aber nur solange du das untere RAM eingeblendet hast, dann im ROM würde ja noch die alte Interrupt Routine stehen.
Title: Re: Wie programmiert man einen Interrupt in Z80
Post by: oobdoo on 15. February 2015, 20:16:33
Ich hatte eigentlich auf einen kleinen Codeschnipsel gehofft. So muß ich ja doch wieder selber denken. Ich weiß nur das ich vor 30 Jahren mal was versucht hatte und das ging nach hinten los.   :(
Title: Re: Wie programmiert man einen Interrupt in Z80
Post by: almasys on 15. February 2015, 21:14:57
'n Abend!

Oh, Z80, geht es überhaupt um den CPC?

Was soll die aufzurufende Routine tun? Von asynchronen Events aus lassen sich die wenigsten Firmwareroutinen nutzen. Muss sie synchron zum Strahlrücklauf aufgerufen werden?

Gute Nacht,
AlMaSys
Title: Re: Wie programmiert man einen Interrupt in Z80
Post by: oobdoo on 16. February 2015, 17:45:29
Ööööhh...  :gruebel:
Da soll einfach nur was regelmäßig aufgerufen werden. Strahlrücklauf ist nicht so wichtig, nur die Regelmäßgkeit vom Aufruf würde ich gerne steuern können. 1x pro Sekunde oder 5x oder nur alle 2 Sekunden. Irgendwie sowas.
Title: Re: Wie programmiert man einen Interrupt in Z80
Post by: oobdoo on 19. February 2015, 14:29:28
Mein erster Interrupt.  :00008351: :00008351: :00008351: :00008351: :00008351:

Gibt (fast) im Sekundentakt den Buchstaben A aus. Heute mal voll stolz auf mich bin.  :jubelaola:

org &4000

InterruptInit:
LD HL,BlocEvent
LD DE,InterruptHandle
LD BC,#8100
call #BCD7
ret

InterruptDone:
LD HL,BlocEvent
call #BCDD
ret
InterruptHandle:
DI
ld a,(counter)
dec a
ld (counter),a
cp 0
jr nz,nixmachen

;; was soll ich hier machen?
ld a,65
call &bb5a
          ld a,50
          ld (counter),a


.nixmachen

          ei
          ret


BlocEvent:
DS 16

.counter
defb 50
Title: Re: Wie programmiert man einen Interrupt in Z80
Post by: oobdoo on 21. February 2015, 16:37:41
Wenn ich keine Fehler beim Abtippen gemacht habe, hier das erste Listing aus https://cpcwiki.de/forum/index.php/topic,282.msg2022.html#msg2022 (https://cpcwiki.de/forum/index.php/topic,282.msg2022.html#msg2022)

org &a000
ld hl,free4
ld bc,tab1
call &bcd1
ret

.free4
defs 4
.tab1
defw ntab

jp init2
jp aus
.ntab
defb "O","N"+&80
defb "O","F","F"+&80
defb 0

.init2
ld a,0
call &bd1c
call &bd19
ld hl,block
call &bce3
di
ld a,6
ld (auf),a
call &bd19
ei
ret

.aus
ld hl,block
call &bce6
ld a,2
call &bd1c
ret

.block
defw 0
defw 0
defb 0
defb &81
defw routi
defb 0
defw 0

.routi
ld a,(auf)
dec a
ld (auf),a
cp 0
jr z,mod0
cp 2
ret nz
ld a,2
jp &bd1c
.mod0
ld a,6
ld (auf),a
ld a,0
jp &bd1c

.auf
defb 6

Title: Re: Wie programmiert man einen Interrupt in Z80
Post by: Devilmarkus on 21. February 2015, 20:08:20
Wer keinen Assembler zur Hand hat:

10 AD=&A000:L=&006A
20 READ a:POKE AD+P,a
30 P=P+1:IF P=L THEN CALL &A000:END
40 GOTO 20
50 DATA &21,&0A,&A0,&01,&0E,&A0,&CD,&D1,&BC,&C9,&00,&00,&00,&00,&16,&A0
60 DATA &C3,&1C,&A0,&C3,&35,&A0,&4F,&CE,&4F,&46,&C6,&00,&3E,&00,&CD,&1C
70 DATA &BD,&CD,&19,&BD,&21,&41,&A0,&CD,&E3,&BC,&F3,&3E,&06,&32,&69,&A0
80 DATA &CD,&19,&BD,&FB,&C9,&21,&41,&A0,&CD,&E6,&BC,&3E,&02,&CD,&1C,&BD
90 DATA &C9,&00,&00,&00,&00,&00,&81,&4C,&A0,&00,&00,&00,&3A,&69,&A0,&3D
100 DATA &32,&69,&A0,&FE,&00,&28,&08,&FE,&02,&C0,&3E,&02,&C3,&1C,&BD,&3E
110 DATA &06,&32,&69,&A0,&3E,&00,&C3,&1C,&BD,&06
Title: Re: Wie programmiert man einen Interrupt in Z80
Post by: oobdoo on 21. February 2015, 20:11:38
Das bekomm ich aber net mit Copy/Paste in den CPC rein.  :zunge0020:
Title: Re: Wie programmiert man einen Interrupt in Z80
Post by: Devilmarkus on 21. February 2015, 20:25:15
Hab mal bissl am Code gefummelt...
So ist er etwas Übersichtlicher:

org &a000

uppermode equ 0
lowermode equ 1
resetmode equ 1

ld hl,free4
ld bc,tab1
call &bcd1
ret

.free4
defs 4
.tab1
defw ntab

jp init2
jp aus
.ntab
defb "O","N"+&80
defb "O","F","F"+&80
defb 0

.init2
ld a,0
call &bd1c
call &bd19
ld hl,block
call &bce3
di
ld a,6
ld (auf),a
call &bd19
ei
ret

.aus
ld hl,block
call &bce6
ld a,resetmode
call &bd1c
ret

.block
defw 0
defw 0
defb 0
defb &81
defw routi
defb 0
defw 0

.routi
ld a,(auf)
dec a
ld (auf),a
cp 0
jr z,mod0
cp 2
ret nz
ld a,lowermode
jp &bd1c
.mod0
ld a,6
ld (auf),a
ld a,uppermode
jp &bd1c

.auf
defb 6


Was mich ja mal interessieren würde:
Bekommt man den Mode-Split um genau 1 Pixelzeile nach Oben verschoben?
Title: Re: Wie programmiert man einen Interrupt in Z80
Post by: oobdoo on 21. February 2015, 21:01:04
Quote from: Devilmarkus on 21. February 2015, 20:25:15
Bekommt man den Mode-Split um genau 1 Pixelzeile nach Oben verschoben?
Was mich interessieren würde ist, kann man überhaupt am CPC auf die gewünschte Rasterzeile warten?
Title: Re: Wie programmiert man einen Interrupt in Z80
Post by: TFM on 23. February 2015, 20:50:30
Ja mit viel Mathematik (nicht so schlimm, Du musst das ja nur einmal berechnen) und das HALT Kommando.

HALT wartet bis ein Interrupt auftritt, es geht also noch dem HALT Kommando im Code dann weiter, wenn sich der Elektronenstrahl auf einer von sechst ganz definierten Stellen befindet.

Um abzufragen ob der Elektronenstrahl gerade zurück läuft kann man folgendes benutzten:


02006                     ;Auf FRAME warten
02007                     ;
02008                     ;Manip. AF, B
02009                     
02010  93B9  06 F5         FRA     LD    B,&F5
02011  93BB                JSZ
02011  93BB  ED 78                 IN    A,(C)
02011  93BD  0F                    RRCA 
02011  93BE  30 FB                 JR    NC,JSZ       ;V-Sync abwarten
02012                     
02013  93C0  C9                    RET   

Title: Re: Wie programmiert man einen Interrupt in Z80
Post by: oobdoo on 25. February 2015, 10:17:40
Sehe ich das richtig, das zwischen einem DI&EI ein HALT ewig warten würde?

Mir ist aber immer noch nicht klar, wie ein Rasterzeileninterrupt auf dem CPC funktioniert.
Hab eh noch meine Schwierigkeiten, das mit einem Interrupt richtig hin zu bekommen (siehe Lustige Bugs in FruttyMan).

Obwohl ich bei den Bildschirmausgaben DI/EI verwende, habe ich dort merkwürdige grafische Effekte.  :(
Title: Re: Wie programmiert man einen Interrupt in Z80
Post by: Devilmarkus on 25. February 2015, 11:51:23
Kann es sein, dass du Speicher aus dem ROM einblendest? Im Screen-RAM?
Title: Re: Wie programmiert man einen Interrupt in Z80
Post by: oobdoo on 25. February 2015, 12:07:09
Quote from: Devilmarkus on 25. February 2015, 11:51:23
Kann es sein, dass du Speicher aus dem ROM einblendest? Im Screen-RAM?
Nein. Ich mache doch nur normales PRINT in Assembler.

org &4000

ld hl,text
call printtext
ret


.PrintText
ld a,(hl)
or a
ret z
call &bb5a
inc hl
jr PrintText

.text
defb 22,1,15,1,129,8,15,3,130,0
Title: Re: Wie programmiert man einen Interrupt in Z80
Post by: oobdoo on 25. February 2015, 13:57:50
Keine Ahnung war mein Frutty gleich zweimal auftaucht.  :motz:
Das macht irgendwie keinen Sinn. Der darf nur auf der Leiter sein!  :motz:
Title: Re: Wie programmiert man einen Interrupt in Z80
Post by: Devilmarkus on 25. February 2015, 15:18:33
Habs gefunden, was ich Dir im Chat schrieb:

Register sichern:
        DI
        PUSH    AF
        PUSH    BC
        PUSH    DE
        PUSH    HL
        PUSH    IX
        PUSH    IY
        EX AF,AF'
        PUSH AF
        EX AF,AF'


Hier nun Deine Routine zum Gegner bewegen usw...
Dann:

Zurückholen:

        ex af,af'
        pop af
        ex af,af'
        pop iy
        pop ix
        pop hl
        pop de
        pop bc
        pop af
        ei
Title: Re: Wie programmiert man einen Interrupt in Z80
Post by: oobdoo on 25. February 2015, 17:19:10
Wenn ich das beim Frutty-Zeichnen reinsetze, dann bleibt das Problem. Nehm ich das auch zusätzlich beim Monster mit rein, dann stürzt das Spiel ab. Interessant.  :gruebel:
Title: Re: Wie programmiert man einen Interrupt in Z80
Post by: TFM on 25. February 2015, 17:54:14
AF' und BC' müssen immer erhalten bleiben. Vielleicht liegt's daran.

Title: Re: Wie programmiert man einen Interrupt in Z80
Post by: oobdoo on 25. February 2015, 18:18:05
Quote from: TFM on 25. February 2015, 17:54:14
AF' und BC' müssen immer erhalten bleiben. Vielleicht liegt's daran.
AF' und BC' hab ich ja nie benutzt.
Title: Re: Wie programmiert man einen Interrupt in Z80
Post by: TFM on 25. February 2015, 18:28:20
War nur so eine Idee.
Title: Re: Wie programmiert man einen Interrupt in Z80
Post by: oobdoo on 25. February 2015, 19:18:41
Ich mache die Ausgabe ja komplett über Steuerzeichen als String, d.h. die Figur wird in einem Rutsch ausgegeben. Wenn dann dabei ein Grafikfehler auftritt, dann muß die Routine &BB5A irgendwo die Interrupts freigeben, so das mein Interrupt dazwischen fummeln kann.
Title: Re: Wie programmiert man einen Interrupt in Z80
Post by: TFM on 25. February 2015, 19:37:11
Das ist richtig &BB5A gibt die Interrupts frei. Kannst Du beides nicht "trennen"?
Title: Re: Wie programmiert man einen Interrupt in Z80
Post by: oobdoo on 25. February 2015, 20:23:25
Quote from: TFM on 25. February 2015, 19:37:11
Das ist richtig &BB5A gibt die Interrupts frei. Kannst Du beides nicht "trennen"?
Nee, da muß ich entweder das BB5A ohne Unterbrechung nachprogrammieren oder die Bildausgabe komplett selber schreiben, wobei ich dann nicht weiß wie ich den Farbwechsel in einem Sprite hinbekommen soll.  :(
Title: Re: Wie programmiert man einen Interrupt in Z80
Post by: oobdoo on 25. February 2015, 21:31:06
Ich gerade im Intern am suchen bei BB5A, kann da aber keine Interruptunterbrechung sehen. Was ich noch im Verdacht habe ist die Soundausgabe. Die werde ich gleich mal wechschalten und mir dann das Ergebniss anschauen.
Title: Re: Wie programmiert man einen Interrupt in Z80
Post by: TFM on 25. February 2015, 22:18:20
Wenn Du mir das nicht glaubst, dann nimm doch mal einen CPC Emulator und lasse den ab BB5A im Debugger-Modus laufen, also Befehl für Befehl. Nach dem RST und folgenden Befehlen wird sehr schnell ein DI und später auch ein EI zu sehen sein.
Title: Re: Wie programmiert man einen Interrupt in Z80
Post by: oobdoo on 25. February 2015, 23:31:53
Quote from: TFM on 25. February 2015, 22:18:20
Wenn Du mir das nicht glaubst, dann nimm doch mal einen CPC Emulator und lasse den ab BB5A im Debugger-Modus laufen, also Befehl für Befehl. Nach dem RST und folgenden Befehlen wird sehr schnell ein DI und später auch ein EI zu sehen sein.
Ja das muß ich wohl mal machen mit dem Debugger, denn in meinen CPC 464 Intern seh ich kein RST mit DI&EI. Aber es ist auch spät, Zeit fürs Bettchen.  :)
Title: Re: Wie programmiert man einen Interrupt in Z80
Post by: oobdoo on 26. February 2015, 09:24:24
So, hab heute mal im Debugger geschaut. Läßt sich der Sprung mit dem RST 8 irgendwie umgehen?
Title: Re: Wie programmiert man einen Interrupt in Z80
Post by: oobdoo on 26. February 2015, 11:29:13
Ich glaub ich habe das Interrupt Problem gelöst. Ich lasse die Interruptroutine zusätzlich über ein Flag pürfen ob sie aktuell überhaupt was machen darf. Und jede Zeichenroutine setzt entsprechend vorher das Flag, bzw. löscht es.