Ready64 Forum
Commodore 64 => Programmazione, Grafica e Musica => Topic aperto da: Alberto - 25 Agosto 2006, 22:05:18
-
I timer di sistema sono una risorsa del C64 potente quanto bistrattata.Si tratta di veri e propri cronometri dotati di una risoluzione pari a un ciclo di clock (circa 1.015 microsecondi per i C64 europei) in grado di contare alla rovescia e di generare interrupt una volta arrivati a0:il C64 ne incorpora ben quattro (due per ogni chip CIA).
Normalmente vengono usati dal sistema operativo per rendere possibile l'interazione con l'utente,la comunicazione con il disk-drive e la trasmissione di dati attraverso la porta del registratore,ma sono talmente versatili da poter essere usati in molti altri contesti,per esempio come cronometri nei videogiochi.
QuestO semplice listato mostra una classica applicazione dei timer di sistema:un piccolo cronometro digitale nell'angolo in alto a sinistra dello schermo,che può essere resettato con la pressione di RUN-STOP.
Come al solito,questa non è una supposta ma una proposta. :) Per chi volesse saperne di più sull'uso dei timer e/o sofisticare la routine,c'e' l'eccellente "Mapping the C64".
P.S. piccola "sfida" per i più volenterosi:siete in grado di sofisticare il programma aggiungendo un tasto per fermare il cronometro per poi farlo ripartire (non da 0,ma dal punto in cui era arrivato a contare),e uno per ripristinare le condizioni standard?
Buon coding a tutti! B)
;esempio d'uso dei timer di sistema
screen = $400
norm = $ea31
ret = $ea81
ref = $fb
.word eop
.word 7102
.byte $9e
.asc "2061"
.byte 0
eop
.word 0
jsr draw
sei
lda #<irq
sta $314
lda #>irq
sta $315 ;irq vect
lda #$7c
sta $dc06
lda #$26
sta $dc07 ;~ 0.01 sec
lda #$82
sta $dc0d ;irq timer B on
lda #1
sta $dc0f ;timer B on
cli
rts
irq
lda $91
bpl rest
cont
lda $dc0d
lsr
bcc skip
jmp norm ;irq normale
skip
ldx #7
ldy #"9"
jsr rfrsh ;centesimi
cmp #"0"
bne exit
dex
jsr rfrsh ;decimi
cmp #"0"
bne exit
dex
dex
jsr rfrsh ;secondi
cmp #"0"
bne exit
dex
ldy #"5" ;0-59 sec
jsr rfrsh ;10 sec
cmp #$30
bne exit
dex
dex
ldy #"9"
jsr rfrsh ;minuti
cmp #$30
bne exit
dex
ldy #"5" ;0-59 min
jsr rfrsh
exit
jsr draw
jmp ret
;aggiorna il conteggio
rfrsh
sty ref
lda time,x
cmp ref
bne ok
lda #$2e ;-2 perchè il carry è settato
ok
adc #1
sta time,x
rts
;stampa il conteggio
draw
ldx #7
lp1
lda time,x
sta screen,x
dex
bpl lp1
rts
;resetta il conteggio
rest
ldx #7
lp2
lda time+9,x
sta time,x
dex
bpl lp2
jmp cont
time
.text "00:00:00"
.byte 0
.text "00:00:00"
-
aggiungendo un tasto per fermare il cronometro per poi farlo ripartire (non da 0,ma dal punto in cui era arrivato a contare
Non ci ho ancora provato, ma immagino che non basti uscire dalla irq con una jmp $ea31 ma proprio fermare il CIA timer? Questo implica doversi memorizzare l'ora in cui si ferma il timer e ripristinarla prima di farlo ripartire, o non dovrebbe essere necessario?
-
Ciao iAN
Questo implica doversi memorizzare l'ora in cui si ferma il timer e ripristinarla prima di farlo ripartire, o non dovrebbe essere necessario?
è un'idea,ma non è necessario stare a diventar matti:in questo caso per fermare il conteggio è sufficiente disattivare gli interrupt del timer B,in questo modo la routine salterà sempre a $ea31.
Altre idee?Forza siori,dai... :mattsid:
-
Ok ho capito,lo faccio io.
Si può inserire nella routine di interrupt un controllo del tipo
lda $91
bpl rest ;RUN-STOP?
cmp #239
beq stop ;SPC?
Come si nota,si salta a stop ogni volta che viene premuto il tasto spazio.
La routine di arresto/riavvio è la seguente (per maggiori informazioni su come si gestisce$dc0d,consultare MTC64).
;ferma/riprende il conteggio
stop
lda #2
sta $dc0d
lda stop+1
eor #$80
sta stop+1
done
jmp ret
Alcune note sul listato completo:l'ultima linea della routine di reset permette di resettare il cronometro anche quando è fermo,mentre lo switch sulla condizione di tasto premuto serve a evitare che,data la frequenza dell'interrupt,il cronometro venga arrestato e/o riavviato più volte ad ogni pressione della spacebar.
Siamo comunque ancora ben lontani dalla perfezione,per cominciare:
1. La tecnica usata per arrestare il cronometro è semplice,ma anche molto approssimativa.
In effetti inibire le IRQ del timer B non significa fermarlo,per cui avremo un errore di anticipo o di ritardo alla ripresa del conteggio.Con una risoluzione di un centesimo di secondo l'errore non è percettibile da un essere umano,ma può diventarlo per risoluzioni maggiori,ad esempio di un secondo.
In quel caso sarebbe molto più saggio arrestare il timer B per poi riavviarlo,come suggerito da iAN CooG.
2. La precisione del cronometro è comunque scarsa,perchè la routine gestisce DUE sorgenti di IRQ,per cui è possibile che di tanto in tanto i due timer si "pestino i piedi":niente di male se il timer A resta in attesa di vedere servita la IRQ del timer B,ma la situazione opposta implica un rallentamento nel conteggio.
3. Scegliere il tasto spazio come interruttore non è il massimo;sarebbe meglio un tasto funzione tipo CTRL altrimenti si può stampare a schermo qualcosa di indesiderato.
Chi vuole provare a migliorare la routine tenga presente che usando CTRL lo switch su $c5 non funzionerà,perchè questa locazione viene modificata solo per evitare letture ripetute di uno stesso carattere.
4. Non è previsto il caso in cui si voglia tornare alle condizioni standard (ancora da implementare,volontari? B) ).
Spero che queste osservazioni possano essere utili a qualcuno.Posto il codice
screen = $400
norm = $ea31
ret = $ea81
ref = $fb
.word eop
.word 7102
.byte $9e
.asc "2061"
.byte 0
eop
.word 0
lda #$40
sta 650 ;disattiva repeat (per SPC)
lda #$f0
sta sw
jsr draw
sei
lda #<irq
sta $314
lda #>irq
sta $315 ;irq vect
lda #$7c
sta $dc06
lda #$26
sta $dc07 ;~ 0.01 sec
lda #$82
sta $dc0d ;irq timer B on
lda #1
sta $dc0f ;timer B on
cli
rts
irq
lda $c5
cmp #$40 ;tasto premuto?
sw
beq cont
lda sw
eor #$20
sta sw
lda $91
bpl rest ;RUN-STOP?
cmp #239
beq stop ;SPC?
cont
lda $dc0d
lsr
bcc skip
jmp norm ;irq normale
skip
ldx #7
ldy #"9"
jsr rfrsh ;centesimi
cmp #"0"
bne exit
dex
jsr rfrsh ;decimi
cmp #"0"
bne exit
dex
dex
jsr rfrsh ;secondi
cmp #"0"
bne exit
dex
ldy #"5" ;0-59 sec
jsr rfrsh ;10 sec
cmp #$30
bne exit
dex
dex
ldy #"9"
jsr rfrsh ;minuti
cmp #$30
bne exit
dex
ldy #"5" ;0-59 min
jsr rfrsh
exit
jsr draw
jmp ret
;aggiorna il conteggio
rfrsh
sty ref
lda time,x
cmp ref
bne ok
lda #$2e ;-2 perchè il carry è settato
ok
adc #1
sta time,x
rts
;stampa il conteggio
draw
ldx #7
lp1
lda time,x
sta screen,x
dex
bpl lp1
rts
;resetta il conteggio
rest
ldx #7
lp2
lda time+8,x
sta time,x
dex
bpl lp2
jmp exit
;ferma/riprende il conteggio
stop
lda #2
sta $dc0d
lda stop+1
eor #$80
sta stop+1
done
jmp ret
time
.text "00:00:0000:00:00"