Ready64 Forum
Commodore 64 => Aiuto & Domande => Topic aperto da: COMMODORO - 28 Settembre 2013, 03:15:20
-
MODERAZIONE:
Non riesumare thread vecchi, è meglio aprire un nuovo thread e inserire un link all'originale.
Thread diviso da: http://ready64.org/smf/index.php?topic=3668
-eregil
Ciao a tutti.
Scusate se riporto in vita questo vecchio topic ma credo ne valga la pena visto che non é stato molto discusso il programma.
Cercavo da niubbone alcuni esempi per capire meglio come creare un charset personalizzato e altrove ho trovato risposte abbastanza soddisfacenti ( anche se mi piacerebbe scrivere un programma in assembly il più breve e chiaro possibile che permetta direttamente di scrivere dal Basic con un charset diverso da quello originale Commodore).
Ordunque cercando di studiarmi questo lavoro per motivi didattici mi sono reso conto pure io nella mia grande niubbità che é un insieme pasticciatissimo di istruzioni che spesso sono messe lì e che non servono a niente. I miei complimenti comunque a Elder (sinceri!) perché nnostante ciò in qualche modo il suo demo funziona(anche se secondo me qualcosa non va proprio come dovrebbe).
Dopo aver fatto partire l'eseguibile ho provato ad assemblare il listato originale a CBM prg Studio ma purtroppo non mi digerisce alcune istruzioni, che io non capisco appieno, tipo:
*=*+1
Cosa significa? L'indirizzo di partenza viene spostato di 1? Come altro posso scriverlo?
*= $5000-26
Scritto così sembra che da un numero esadecimale venga sottratto un numero decimale. Sono (ovviamente) due numeri entrambi esadecimali?
Passiamo al listato:
lda #$18 ; screen at $0400 chars a $2000
sta $d016 ;multicolor!
Ovviamente qua c'é un pasticcio. $18 andava assegnato ad $d018, come infatti succede qualche riga sotto. A $d016 Elder voleva assegnare qualcos'altro.
Questo 'ambo' di righe viene ripetuto ancora nel corso del listato, un copincolla davvero malfatto.
lda #$1b
ldx #$08
ldy #$18
sta $d011 ; text mode
sty $d018 ; screen a $0400, charset a $2000
Che senso ha quel ldx #$08 se non viene assegnato a niente?
Oppure non ci ho capito io qualcosa di quello che ho studiato, ma qualche riga sotto un bel ldx#$ff toglie pochi dubbi al pasticcio.
Se qualcuno si prende la briga mi spiega se ha un senso, o non molto del perché la prima istruzione 'sei' viene data a riga 11 e la rispettiva 'cli' a riga 132? E' normale? O bastava mettere la 'sei' all'inizio delle istruzioni di interrupt?
Dalla riga 145 ritroviamo queste:
lda #$1b
ldx #$08
ldy #$18
sta $d011 ; Clear high bit di $d012: text mode
sty $d018
lda #$18 ; screen at $0400 chars a $2000
sta $d016 ; multicolor!
A che serve ripetere? Ritroviamo anche ldx #$08 e company.
stx counter
lda $212e,x ; byte xesimo dai caratteri
eor $ff ; inverti
sta $212e,x ; store al suo posto
inx
lda #<int2
ldx #>int2
sta $0314
stx $0315
ldy #$92
sty $d012
asl $d019
pla
tay
pla
tax
pla
rti
Qua c'é una chiamata ad un interrupt. E' normale che non sia preceduto e seguito da sei e cli ?
Funziona, ma a proprio rischio e pericolo?
Mi fermo qui, il listato é lungo, nel frattempo spero che qualcuno abbia voglia di togliermi dubbi e dare conferme alle mie certezze!
-
Tante istruzioni pla, nessun pha.
Suppongo che fra riga 175 e 179, e fra 219 e 223 le 'pla' siano da sostituire con 'pha'.
Riga 364-369:
ldy #$0
sty $d012
;rimetto apposto anche il registro $d012
lda #$34
sta $d012
Viene impostato $d012 due volte? Ha un senso?
-
Se ti poni domande perché stai cercando di imparare va tutto bene, ma prima di "proporre modifiche" hai bisogno di rivederti un po' di basi, cerca prima di capire cosa fa il codice e possibilmente inizia da esempi più semplici.
In generale, le istruzioni SEI e CLI si utilizzano dove modifichi i puntatori alla routine di interrupt, non nella routine di interrupt stessa.
L'istruzione PHA non è l'unico modo in cui si mette qualcosa nello stack, perciò non deve necessariamente esserci un "equilibrio" tra numero di istruzioni PHA e numero di istruzioni PLA.
Scrivendo sul registro $d012, si modifica il numero di rasterline al quale viene chiamata la routine di interrupt relativa al raster. Senza entrare in maggiori dettagli, diciamo pure semplicemente che il comportamento di questo registro è "particolare" per cui in certi casi ha perfettamente senso impostarlo più volte in una routine come in questo demo.
Separo perché il thread di origine non deve essere "dirottato" su domande sulle basi.
-
Ciao Eregil e grazie per le risposte! (mi sono accorto solo adesso del topic credevo che avessi cancellato tutto).
Grazie per la spiegazione su pha e pla e il resto!
Non vorrei annoiarti ma vorrei cercare di capire come funziona così come é impostata la serie di interrupts del programma.
L'interrupt installato punta a 'int', all'interno del quale un interrupt punta a 'int2', all'interno del quale un interrupt punta a 'intsprites', all'interno del quale un interrupt punta a 'int3' che scrive così:
int3
lda # sta $0314
sta $d019
inc $d012
cli
eccetera..
1) Perché int3 punta solo al 'low address' dell'interrupt int4?
2) Perché solo la dichiarazione dell'interrupt 'int' é all'interno di un sei..cli? Forse perché le altre chiamate ad interrupts sono nidificate all'interno, e quindi eseguite nel tempo di interrupt di 'int' e quindi non ci sono rischi?
3) perché la chiamata di int4 all'interno di int3 é chiusa da un'istruzione 'cli' senza che fosse stata data un'struzione 'sei'?
Ringrazio eregil o altri che volessero chiarirmi le idee!
-
Guarda, sul demo specifico eventualmente Elder interverrà se è ancora attivo e ha il tempo di farlo. Dato che personalmente conosco l'uso del raster proprio un minimo sindacale, e dovrei analizzare il codice meglio di quanto la mia disponibilità di tempo permette, mi limito a qualche indicazione che può avere carattere generale.
Quando si opera col raster è una tecnica abbastanza comune modificare il puntatore alla routine di interrupt di volta in volta, per lanciare routine diverse in base alla posizione del raster.
Inoltre, quando si utilizza il raster per produrre certi effetti visivi, càpita di dover scrivere il codice assembly contando il numero di cicli macchina che saranno impiegati ad eseguirlo, perché si ha la necessità di completare una determinata operazione esattamente in un momento particolare in cui il raster raggiunge la linea che ci interessa. Vi è un certo grado di incertezza su quando inizia l'esecuzione della routine di interrupt, ciò è dovuto al fatto che il 6510 entra nella routine di interrupt sempre dopo aver completato l'operazione corrente, la quale può richiedere ancora un numero variabile di cicli macchina. Perciò è necessario fare in modo che questo grado di incertezza sia eliminato in qualche modo.
Quindi in risposta alle tue domande:
1) è possibile che nel punto del codice in cui viene modificato solo il byte basso del puntatore, avvenga così perché è noto al programmatore che il byte alto è già impostato correttamente; è una piccola ottimizzazione, se vogliamo
2,3) questo ha a che fare con la tecnica del "double interrupt", che io non padroneggio e quindi che evito di descrivere dettagliatamente, onde non dire cose fuorvianti :) mi limito a dire che il principio è che per eliminare l'incertezza di cui sopra vogliamo che un interrupt avvenga mentre è ancora in corso l'esecuzione di una sequenza di NOP nella nostra routine di interrupt corrente, e questo richiede che eseguiamo una CLI al momento giusto, mentre una SEI in questo contesto non è necessaria.
-
Mi permetto di aggiungere qualche nota a quanto ha già detto Eregil.
Innanzitutto una premessa che ritengo doverosa: la minidemo in oggetto è uno dei primi lavori di Elder, è ancora "grezza" e non da la misura di quello che sa fare ora.
Il pezzo di codice accennato serve in effetti ad ottenere la stabilizzazione a doppio raster. Ti spiego sinteticamente come funziona:
- la routine int3, in risposta ad un raster interrupt, viene eseguita con un'incertezza di 0/6 (7 con gli illegal codes) cicli rispetto al preciso momento di trigger (in base all'ultima istruzione incontrata prima dell'irq).
- All'interno di int3 si imposta un nuovo raster irq nella riga successiva ma si fa in modo che questo secondo irq capiti durante istruzioni brevi in modo da ridurre l'incertezza a 0/1 cicli. Le nop - eseguite in 2 cicli - servono a questo: l'irq può capitare prima di una nop (2 cicli di ritardo) o in mezzo (3 cicli di ritardo).
- All'interno di int4, infine, basterà fare un opportuno controllo (quello di questo programma in effetti è sbagliato) del valore raster a cavallo tra la riga di chiamata e la successiva per assorbire l'ultima incertezza ed avere una routine perfettamente sincronizzata al video.
Ora, int3 è un po' criptico perchè c'è poco tempo a disposizione.
Alcune cose le ha già dette Eregil, le ripeto solo per elencarle tutte:
- Perchè c'è poco tempo? Perchè in risposta ad un irq il 64 esegue la routine puntata in $fffe/$ffff che quando, come in questo, caso il kernal è attivo, punta a $ff48. In $ff48 c'è un pezzo di codice che "costa" 29 cicli che sommati ai 7 della irq e ai 2 minimi di ritardo nel servire un irq fanno un totale di 38. D'altra parte l'incertezza dell'irq è di 0-6, quindi nel peggiore dei casi abbiamo solo 63-38-6=19 cicli a disposizione: pochi!!
- int3 e int4 sono volutamente nella stessa pagina (negli stessi 256 byte) perciò è sufficiente impostare il low byte dell'irq handler (lda+sta, 6 cicli risparmiati)
- int4 è dispari (align 2 *=*+1): con align 2 mi assicuro di avere un indirizzo pari, con *=*+1 passo al byte successivo. Mi serve dispari perchè così con una sta $d019 effettuo l'acknowledge del raster irq (il bit 0 è a 1). (lda, 2 cicli risparmiati)
- la inc $d012 serve a triggerare un nuovo raster nella linea successiva a quella corrente
- la cli è necessaria perchè, quando viene servito un irq, il 6510 imposta automaticamente la maschera (bit I dello SR attivo). In pratica dopo aver salvato lo SR e l'IP nello stack è come se venisse eseguita un'implicita "sei". Perchè? Il 6510 esegue "l'istruzione" irq quando il piedino irq è negato; d'altra parte se la periferica che ha abbassato il livello del piedino irq non viene "avvisata" (sta $d019 in questo caso) che è stata servita, il livello verrà mantenuto. In sostanza se il 6510 non attivasse la maschera si entrerebbe in un loop infinito di irq perchè la condizione che lo genera sarebbe sempre valida.
-
Beh ragazzi mi inchino commmosso alla vostra disponibilità e alle vostre conoscenze. Mi ci vorrà un pò di tempo per digerire il malloppo generosamente fornito da Freshness, tanta roba! Spero di avere ancora così tanta disponibilità per le tante domande che vorrei fare ( soprattutto spiegazioni sui listati forniti da Freshness e da Ian Coog perché sinceramente su tutte le decine di testi online che ho consultato non si arriva a decifrare che una parte di quanto scritto e sinceramente nemmeno cercando in profondità su Internet: forse dovrei studiarmi a fondo i contenuti di codebase64? Se avete altri siti o indirizzi da propormi per capire di più ve ne sarei grato!)
Ciao!
-
Yep, sono sempre attivo :) Freshness ha già risposto chiaramente a tutti i quesiti tecnici riguardanti il timing.
Per quanto riguarda il resto, era una delle mie primissime prove e ci sono diversi errori di distrazione, e "pasticci". Nel C64 world, le cose da tenere a mente sono veramente tante, e all'inizio può capitare di scrivere un pò di codice senza sapere al 100% l'effetto di ogni istruzione, e quindi può capitare di pasticciare.
Il post non era inteso come tutorial (avevo iniziato da un mese forse). La mia intenzione era far vedere il mio codice da niubbo ai più esperti, in modo da avere dritte e consigli.
In ogni caso, riguardo il charset, le cose da sapere sono queste:
1) il charset deve essere allineato a $800, per cui puoi importarlo a indirizzi come
$2000
$2800
$3000
ecc ecc. Per intenderci $2100 non va bene.
2) una volta importato il charset in una locazione di memoria consona (ad es $2000), devi ricordare che il VIC indicizza solo 16kb di memoria "per volta".
I 16 kb visibili dal VIC sono gestiti da $DD00 (bit 0 e 1), per cui si ha:
$DD00 = %xxxxxx11 -> bank0: $0000-$3fff
$DD00 = %xxxxxx10 -> bank1: $4000-$7fff
$DD00 = %xxxxxx01 -> bank2: $8000-$bfff
$DD00 = %xxxxxx00 -> bank3: $c000-$ffff
fonte Codebase64 (http://codebase64.org/doku.php?id=base:vicii_memory_organizing))
Quindi, se il tuo charset è a $2000 e lo schermo è a $0400, il VIC deve "puntare" al bank 0. Quindi:
lda $dd00
and #%11111100
ora #%00000011;BANK 0
sta $DD00
Dopodichè si tratta di calcolare il valore giusto di $d018.
Sempre consultando le tabelle nel link di prima, si ha:
%0001xxxx schermo a $0400
%xxxx100x charset a $2000
combinando i due nybble, si ottiene
%00011000 che in hex è $18.
Quindi il codice diventa
lda $dd00
and #%11111100
ora #%00000011;BANK 0
sta $DD00
lda #$18
sta $d018
Tieni a mente che il charset e lo schermo devono essere nello stesso banco, altrimenti il giochino non funziona.
Alterare il charset in tempo reale, o selezionare charset diversi a seconda della rasterline sono dei tricks molto comuni nei demo, per cui è bene imparare almeno le basi prima di addentrarsi in queste cose.
-
Ciao Elder!
Se quello era un tuo prodotto dopo solo un mese di smanettamento ti faccio i miei complimenti.
Abbiamo approcci piuttosto diversi nell'apprendere l'assembly, mi sembra che tu ti avventuravi in cose complesse che erano oltre le tue possibilità di allora, infatti come scrivi tu stesso alle volte facevi cose senza sapere bene i risultati. io uso invece solo quello che so, e mi rendo conto di fare progressi molto lenti, ciò che mi consentono i neuroni sani rimastimi.
Ci sono altri tuoi lavori più recenti al di fuori di questo Forum che si possono andare a spiare?Grazie e a presto!
P.s. avevi fatto giusto il listato, ma sbagliato la riga del commento, questa:
lda #$18 ; screen at $0400 chars a $2000
sta $d016 ;multicolor!
Ovviamente era più sotto
ldy #$18 ; screen at $0400 chars a $2000
sty $d018
Ciao!
-
Tra qualche tempo uscirà un demo molto lungo che sto realizzando con Freshness, a seguito dell'uscita pubblicheremo tutto il source su un repository git! a presto
-
Tra qualche tempo uscirà un demo molto lungo che sto realizzando con Freshness, a seguito dell'uscita pubblicheremo tutto il source su un repository git! a presto
Ottimo! Aspettiamo il vostro lavoro! Ciao!