L'impostazione generale é corretta, i problemi sono dovuti ai timing.
Punto 1Nel 6502 (ed analogamente nel 6510 del nostro c64), quando si genera un'interruzione avvengono le seguenti cose:
- si attende che finisca l'eventuale istruzione in corso (X cicli)
- si esegue l'IRQ (salvataggio IP e status register, salto alla routine puntata da $FFFE/$FFFF) (7 cicli)
Ora, se stai lavorando col c64 in modalità normale (con le ROM attive), agli indirizzi $FFFE ed $FFFF ci sono rispettivamente i valori $48 e $FF. Il processore, quindi, non farà altro che saltare alla routine all'indirizzo $FF48: questa routine é quella che ho già postato nella mia prima risposta a questo thread. Se lavorassi con le ROM inattive (non é il caso del tuo programma) potresti iniziare a servire l'interruzione dopo un ritardo di soli 7+X cicli.
- si esegue la routine in $FF48 che, come ultima istruzione, effettua un salto all'indirizzo puntato in $0314/$0315 (29 cicli)
Tirando le somme, quindi, in realtà l'istruzione "lsr $d019", la prima del tuo irqpre, viene eseguita dopo X+7+29 cicli da quando é stata generata l'interruzione (cioé da quando siamo nella riga $34!).
Questo 'X' dipende da quanto lunga era l'istruzione durante la quale é stato generato l'IRQ: può valere da 0 (interruzione avvenuta esattamente tra un'istruzione e l'altra) a 7 (interruzione avvenuta durante il primo ciclo di un'istruzione da 8 cicli).
Da ciò ne consegue che l'inizio di irqpre si attesta tra i 36 ed 43 cicli di ritardo rispetto all'inizio del raster.
Cosa comporta ciò? Che nel peggior caso hai solo 20 (63-43) cicli a disposizione per gestire la prima interruzione prima che sia triggerato l'interrupt per la seconda (quella relativa alla riga $35).
Tornando al tuo programma con un rapido conto puoi osservare che le istruzioni che, in irqpre, vanno dalla LSR $D019 alla CLI (compresa) superano abbondantemente i 20 cicli (sono già 20 con lo sta $0314).
Hai pochi cicli... come fare? C'é posto solo per l'essenziale: l'aknowledge dell'interrupt, l'impostazione della riga del secondo interrupt, l'aggiornamento dell'indirizzo della routine dell'handler.
Quest'ultima attività in particolare la puoi ridure al solo aggiornamento del LSB perché, se allinei bene il programma, é ragionevole pensare che il primo interrupt sia negli stessi 256 byte del secondo.
Quindi: 6 cicli per $D019, 6 per $D012,2+4 per $0314,2 per il CLI:20 cicli!
Fai quindi seguire almeno 4 NOP: noi abbiamo analizzato il caso peggiore, ma nel caso migliore avremmo 36+20=56 cicli e quindi dopo la CLI ci potrebbero essere ancora 7 cicli prima della seconda interruzione. Con 4 NOP siamo al sicuro.
Tutto questo cosa ci garantisce? Che la seconda interruzione cadrà:
- o tra una NOP e l'altra (Ritardo 0 cicli)
- o in mezzo ad una NOP (Ritardo 1 ciclo)
Il secondo interrupt ha lo scopo di sistemare questa incertezza: anche in questo caso i timing del tuo programma non sono corretti ma attendo la sistemazione di irqpre prima di chiarire il meccanismo (con quello che sarà il
punto 2).
Perdona la logorrea, spero di averti chiarito un po' le idee!
Attendo (attendiamo) nuove!